Add http api basics

This commit is contained in:
Him188 2019-11-21 21:31:05 +08:00
parent 5ca07d3749
commit d261cd7b7e
5 changed files with 215 additions and 122 deletions

3
mirai-api-http/README.md Normal file
View File

@ -0,0 +1,3 @@
# mirai-api-http
Coroutine-based Http API adapter for Mirai.

View File

@ -0,0 +1,75 @@
@file:Suppress("UNUSED_VARIABLE")
plugins {
id("kotlinx-atomicfu")
kotlin("jvm")
id("kotlinx-serialization")
}
group = "net.mamoe.mirai"
version = "0.1.0"
description = "Mirai Http Api"
val kotlinVersion: String by rootProject.ext
val atomicFuVersion: String by rootProject.ext
val coroutinesVersion: String by rootProject.ext
val kotlinXIoVersion: String by rootProject.ext
val coroutinesIoVersion: String by rootProject.ext
val klockVersion: String by rootProject.ext
val ktorVersion: String by rootProject.ext
val serializationVersion: String by rootProject.ext
fun org.jetbrains.kotlin.gradle.plugin.KotlinDependencyHandler.kotlinx(id: String, version: String) {
implementation("org.jetbrains.kotlinx:$id:$version")
}
fun org.jetbrains.kotlin.gradle.plugin.KotlinDependencyHandler.ktor(id: String, version: String = ktorVersion) {
implementation("io.ktor:$id:$version")
}
kotlin {
sourceSets["main"].apply {
dependencies {
implementation(project(":mirai-core"))
kotlin("kotlin-stdlib-jdk8", kotlinVersion)
kotlin("kotlin-stdlib-jdk7", kotlinVersion)
kotlin("kotlin-reflect", kotlinVersion)
ktor("ktor-server-cio")
kotlinx("kotlinx-io-jvm", kotlinXIoVersion)
ktor("ktor-http-jvm")
}
}
sourceSets["test"].apply {
dependencies {
}
kotlin.outputDir = file("build/classes/kotlin/jvm/test")
kotlin.setSrcDirs(listOf("src/$name/kotlin"))
}
sourceSets.all {
languageSettings.enableLanguageFeature("InlineClasses")
languageSettings.useExperimentalAnnotation("kotlin.Experimental")
dependencies {
kotlin("kotlin-stdlib", kotlinVersion)
kotlin("kotlin-serialization", kotlinVersion)
kotlinx("atomicfu", atomicFuVersion)
kotlinx("kotlinx-io", kotlinXIoVersion)
kotlinx("kotlinx-coroutines-io", coroutinesIoVersion)
kotlinx("kotlinx-coroutines-core", coroutinesVersion)
kotlinx("kotlinx-serialization-runtime", serializationVersion)
ktor("ktor-server-core")
ktor("ktor-http")
}
}
}

View File

@ -0,0 +1,136 @@
package net.mamoe.mirai.api.http
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.HttpMethod
import io.ktor.http.HttpStatusCode
import io.ktor.response.respond
import io.ktor.routing.Route
import io.ktor.routing.route
import io.ktor.routing.routing
import io.ktor.server.engine.applicationEngineEnvironment
import io.ktor.util.pipeline.ContextDsl
import io.ktor.util.pipeline.PipelineContext
import io.ktor.util.pipeline.PipelineInterceptor
import net.mamoe.mirai.Bot
import net.mamoe.mirai.addFriend
import net.mamoe.mirai.getGroup
import net.mamoe.mirai.getQQ
import net.mamoe.mirai.utils.io.hexToBytes
import net.mamoe.mirai.utils.io.hexToUBytes
fun main() {
Application(applicationEngineEnvironment {}).apply { mirai() }
}
@UseExperimental(ExperimentalUnsignedTypes::class)
fun Application.mirai() {
install(DefaultHeaders)
install(CallLogging)
routing {
mirai("/sendFriendMessage") {
// TODO: 2019/11/21 解析图片消息等为 Message
Bot.instanceWhose(qq = param("bot")).getQQ(param("qq")).sendMessage(param<String>("message"))
call.ok()
}
mirai("/sendGroupMessage") {
Bot.instanceWhose(qq = param("bot")).getGroup(param<UInt>("group")).sendMessage(param<String>("message"))
call.ok()
}
mirai("/event/message") {
// TODO: 2019/11/21
Bot.instanceWhose(qq = param("bot"))
}
mirai("/addFriend") {
Bot.instanceWhose(qq = param("bot")).addFriend(
id = param("qq"),
lazyMessage = paramOrNull<String?>("message")?.let { { it } } ?: { "" },
lazyRemark = paramOrNull<String?>("remark")?.let { { it } } ?: { "" }
)
call.ok()
}
}
}
@ContextDsl
private fun Route.mirai(path: String, body: PipelineInterceptor<Unit, ApplicationCall>): Route {
return route(path, HttpMethod.Get) {
handle {
try {
this.body(this.subject)
} catch (e: IllegalAccessException) {
call.respond(HttpStatusCode.BadRequest, e.message)
}
}
}
}
@Suppress("NOTHING_TO_INLINE")
suspend inline fun ApplicationCall.ok() = this.respond(HttpStatusCode.OK, "OK")
/**
* 错误请求. 抛出这个异常后将会返回错误给一个请求
*/
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)
}
/**
* 错误参数
*/
class IllegalParamException(message: String) : IllegalAccessException(message)
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)
private inline fun <reified R> PipelineContext<Unit, ApplicationCall>.param(name: String): R = this.paramOrNull(name) ?: illegalParam(R::class.simpleName, name)
@Suppress("IMPLICIT_CAST_TO_ANY")
@UseExperimental(ExperimentalUnsignedTypes::class)
private inline fun <reified R> PipelineContext<Unit, ApplicationCall>.paramOrNull(name: String): R? =
when {
R::class == Byte::class -> call.parameters[name]?.toByte()
R::class == Int::class -> call.parameters[name]?.toInt()
R::class == Short::class -> call.parameters[name]?.toShort()
R::class == Float::class -> call.parameters[name]?.toFloat()
R::class == Long::class -> call.parameters[name]?.toLong()
R::class == Double::class -> call.parameters[name]?.toDouble()
R::class == Boolean::class -> when (call.parameters[name]) {
"true" -> true
"false" -> false
"0" -> false
"1" -> true
null -> null
else -> illegalParam("boolean", name)
}
R::class == String::class -> call.parameters[name]
R::class == UByte::class -> call.parameters[name]?.toUByte()
R::class == UInt::class -> call.parameters[name]?.toUInt()
R::class == UShort::class -> call.parameters[name]?.toUShort()
R::class == UByteArray::class -> call.parameters[name]?.hexToUBytes()
R::class == ByteArray::class -> call.parameters[name]?.hexToBytes()
else -> error(name::class.simpleName + " is not supported")
} as R?

View File

@ -1,122 +0,0 @@
kotlin {
targets {
fromPreset(presets.jvm, "jvm")
//fromPreset(presets.jvm, "android")
//fromPreset(presets.mingwX64, "mingwX64")
}
jvm{
withJava()
}
/*
mingwX64("mingwX64") {
binaries {
executable {
// Change to specify fully qualified name of your application's entry point:
entryPoint = 'hex.main'
// Specify command-line arguments, if necessary:
runTask?.args('')
}
}
}*/
sourceSets {
commonMain {
dependencies {
api group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib-common', version: kotlinVersion
api group: 'org.jetbrains.kotlin', name: 'kotlin-reflect', version: kotlinVersion
api group: 'org.jetbrains.kotlinx', name: 'kotlinx-coroutines-core-common', version: coroutinesVersion
api group: 'org.jetbrains.kotlinx', name: 'atomicfu-common', version: atomicFuVersion
api group: 'org.jetbrains.kotlinx', name: 'kotlinx-io', version: kotlinXIoVersion
api group: 'org.jetbrains.kotlinx', name: 'kotlinx-coroutines-io', version: coroutinesIoVersion
implementation "com.soywiz.korlibs.klock:klock:$klockVersion"
api group: 'io.ktor', name: 'ktor-client-core', version: ktorVersion
api group: 'io.ktor', name: 'ktor-network', version: ktorVersion
//api group: 'io.ktor', name: 'ktor-client-cio', version: ktorVersion
//api group: 'io.ktor', name: 'ktor-client', version: ktorVersion
api group: 'io.ktor', name: 'ktor-http', version: ktorVersion
//api group: 'io.ktor', name: 'ktor-utils', version: ktorVersion
//api group: 'io.ktor', name: 'ktor-io', version: ktorio_version
}
}
jvmMain {
apply plugin: 'java'
dependencies {
api group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib', version: kotlinVersion
api group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib-jdk8', version: kotlinVersion
api group: 'org.jetbrains.kotlin', name: 'kotlin-reflect', version: kotlinVersion
api group: 'org.jetbrains.kotlinx', name: 'kotlinx-coroutines-core', version: coroutinesVersion
api group: 'org.jetbrains.kotlinx', name: 'atomicfu', version: atomicFuVersion
api group: 'org.jetbrains.kotlinx', name: 'kotlinx-io', version: kotlinXIoVersion
// api group: 'org.jetbrains.kotlinx', name: 'kotlinx-io-jvm', version: kotlinXIoVersion
api group: 'org.jetbrains.kotlinx', name: 'kotlinx-coroutines-io', version: coroutinesIoVersion
api group: 'io.ktor', name: 'ktor-http-cio', version: ktorVersion
api group: 'io.ktor', name: 'ktor-http', version: ktorVersion
api group: 'io.ktor', name: 'ktor-client-core-jvm', version: ktorVersion
api group: 'io.ktor', name: 'ktor-client-cio', version: ktorVersion
implementation 'org.yaml:snakeyaml:1.18'
implementation 'org.jsoup:jsoup:1.12.1'
implementation 'org.ini4j:ini4j:0.5.2'
}
}
/*
mingwX64Main {
dependencies {
// https://mvnrepository.com/artifact/org.jetbrains.kotlin/kotlin-reflect
implementation rootProject.ext.kotlinCommon
implementation rootProject.ext.coroutine
implementation rootProject.ext.coroutineNative
implementation rootProject.ext.kotlinNative
implementation rootProject.ext.reflect
//implementation rootProject.ext.coroutine
implementation rootProject.ext.kotlinxIONative
}
}*/
jvmTest {
apply plugin: 'java'
}
androidMain{
dependencies{
api 'com.google.android:android:4.1.1.4'
api 'com.android.support:support-annotations:26.1.0'
api group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib', version: kotlinVersion
api group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib-jdk8', version: kotlinVersion
api group: 'org.jetbrains.kotlin', name: 'kotlin-reflect', version: kotlinVersion
api group: 'org.jetbrains.kotlinx', name: 'kotlinx-coroutines-core', version: coroutinesVersion
api group: 'org.jetbrains.kotlinx', name: 'atomicfu', version: atomicFuVersion
api group: 'org.jetbrains.kotlinx', name: 'kotlinx-io', version: kotlinXIoVersion
// api group: 'org.jetbrains.kotlinx', name: 'kotlinx-io-jvm', version: kotlinXIoVersion
api group: 'org.jetbrains.kotlinx', name: 'kotlinx-coroutines-io', version: coroutinesIoVersion
api group: 'io.ktor', name: 'ktor-http-cio', version: ktorVersion
api group: 'io.ktor', name: 'ktor-http', version: ktorVersion
api group: 'io.ktor', name: 'ktor-client-core-jvm', version: ktorVersion
api group: 'io.ktor', name: 'ktor-client-cio', version: ktorVersion
}
}
all {
languageSettings.enableLanguageFeature("InlineClasses")
}
}
}
compileKotlinJvm {
}
compileTestJava{
}

View File

@ -4,6 +4,7 @@ include(':mirai-core')
include(':mirai-console')
include(':mirai-api')
include(':mirai-api-http')
include(':mirai-demos:mirai-demo-1')
include(':mirai-demos:mirai-demo-gentleman')
include(':mirai-demos')