Skip to content

Commit

Permalink
Merge pull request #1 from lz1998/master
Browse files Browse the repository at this point in the history
update
  • Loading branch information
lz1998 committed Oct 2, 2020
2 parents a372954 + 9826255 commit db12338
Show file tree
Hide file tree
Showing 20 changed files with 346 additions and 278 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ out/
### MIRAI ###
device.json
src/main/java/onebot/
src/main/java/dto/


### Mac ###
Expand Down
29 changes: 21 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,27 @@
# Spring-Mirai-Client
可通过HTTP请求创建Mirai QQ机器人,处理登陆验证码、URL等信息

本项目(client端)使用 Kotlin 编写(因为 Mirai 是 kotlin 的)
用于收发QQ消息,并通过 websocket + protobuf 上报给 server 进行处理。

server 端:可使用任意语言编写
本项目对应前端:https://github.com/protobufbot/spring-mirai-client-ui

通信协议:https://github.com/lz1998/onebot/tree/master/v11/specs/idl
server端可以使用任意语言编写,通信协议:https://github.com/lz1998/onebot_idl

## TODO
- 整合 onebot 协议,与远程服务器通过 http/websocket/rpc 方式进行通信
- 群/私聊 消息事件 -> protobuf/json
- 接受API调用,执行并返回结果
Java/Kotlin用户推荐使用 [spring-boot-starter](https://github.com/protobufbot/pbbot-spring-boot-starter)

支持发送的消息:文字、表情、图片、闪照、atQQ、atAll

## 使用说明

下载release:https://github.com/ProtobufBot/Spring-Mirai-Client/releases

解压后运行
```bash
java -jar spring-mirai-client-版本.jar
```

浏览器打开 http://localhost:9000/

创建机器人并处理下方验证码(图形验证码或设备锁)


![截图](https://github.com/lz1998/Spring-Mirai-Client/blob/master/screenshot/client.jpg?raw=true)
26 changes: 21 additions & 5 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ plugins {
}

group = "net.lz1998"
version = "0.0.1-SNAPSHOT"
version = "0.0.3"
java.sourceCompatibility = JavaVersion.VERSION_1_8

configurations {
Expand All @@ -41,16 +41,19 @@ repositories {
maven(url = "http://maven.aliyun.com/nexus/content/repositories/jcenter")
mavenCentral()
jcenter()
maven(url = "http://repo.spring.io/plugins-release")
// maven(url = "http://repo.spring.io/plugins-release")
maven(url = "https://plugins.gradle.org/m2/")
}

dependencies {
implementation("net.mamoe:mirai-core-qqandroid:1.3.0")
implementation("net.mamoe:mirai-core-qqandroid:1.3.1")
implementation("com.squareup.okhttp3:okhttp:4.8.0")
implementation("com.google.protobuf:protobuf-java:3.12.2")
// implementation("com.google.protobuf:protobuf-javalite:3.8.0")

implementation("com.google.protobuf:protobuf-java:3.12.2")
implementation("com.googlecode.protobuf-java-format:protobuf-java-format:1.4")
implementation("com.google.protobuf:protobuf-java-util:3.12.2")
// implementation("com.googlecode.protobuf:protobuf-java-format:1.2")

implementation("org.springframework.boot:spring-boot-starter-web")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
Expand All @@ -67,7 +70,20 @@ dependencies {
protobuf {
generatedFilesBaseDir = "$projectDir/src"
println(generatedFilesBaseDir)
protoc { artifact = "com.google.protobuf:protoc:3.7.0" }
protoc {
// You still need protoc like in the non-Android case
artifact = "com.google.protobuf:protoc:3.8.0"
}
generateProtoTasks {
all().forEach {
it.builtins{
// remove("java")
// id("java"){
// option("lite")
// }
}
}
}
}
sourceSets {
main {
Expand Down
Binary file added screenshot/client.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
package net.lz1998.mirai

import net.lz1998.mirai.properties.ClientProperties
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.boot.runApplication
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.http.converter.protobuf.ProtobufHttpMessageConverter

@SpringBootApplication
@EnableConfigurationProperties(ClientProperties::class)
class SpringMiraiClientApplication

fun main(args: Array<String>) {
Expand Down
13 changes: 13 additions & 0 deletions src/main/kotlin/net/lz1998/mirai/config/ProtobufConfig.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package net.lz1998.mirai.config

import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.http.converter.protobuf.ProtobufHttpMessageConverter

@Configuration
class ProtobufConfig {
@Bean
fun createProtobufConverter(): ProtobufHttpMessageConverter {
return ProtobufHttpMessageConverter();
}
}
57 changes: 20 additions & 37 deletions src/main/kotlin/net/lz1998/mirai/controller/BotController.kt
Original file line number Diff line number Diff line change
@@ -1,57 +1,40 @@
package net.lz1998.mirai.controller

import kotlinx.coroutines.runBlocking
import dto.HttpDto
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import net.lz1998.mirai.service.BotService
import net.lz1998.mirai.service.LoginDataType
import net.lz1998.mirai.service.myLoginSolver
import net.lz1998.mirai.service.MyLoginSolver
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.http.MediaType
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.bind.annotation.*

@RestController
@RequestMapping("/bot")
class BotController {

@Autowired
lateinit var botService: BotService


@RequestMapping("/createBot")
fun createBot(botId: Long, password: String): String {
println(botId)
println(password)
runBlocking { // TODO 是否可以优化? suspend报错怎么解决?
botService.createBot(botId, password)
}
return "ok"
// 创建一个机器人并登陆
@RequestMapping("/create/v1", produces = ["application/x-protobuf"], consumes = ["application/x-protobuf"])
fun createBot(@RequestBody param: HttpDto.CreateBotReq): HttpDto.CreateBotResp {
GlobalScope.launch { botService.createBot(param.botId, param.password) }
return HttpDto.CreateBotResp.newBuilder().build()
}

// 通过轮询获取登陆验证url
@RequestMapping("/getLoginUrl")
fun getLoginUrl(botId: Long): String? {
val loginData = myLoginSolver.getLoginData(botId) ?: return null
return if (loginData.type != LoginDataType.PIC_CAPTCHA) {
loginData.url
} else {
null
}
@RequestMapping("/list/v1", produces = ["application/x-protobuf"], consumes = ["application/x-protobuf"])
fun listBot(): HttpDto.ListBotResp {
val botList = botService.listBot()
return HttpDto.ListBotResp.newBuilder().addAllBotList(botList).build()
}

// 通过轮询获取登陆图片验证码
@RequestMapping("/getLoginImage", produces = [MediaType.IMAGE_JPEG_VALUE])
fun getLoginData(botId: Long): ByteArray? {
val loginData = myLoginSolver.getLoginData(botId) ?: return null
return if (loginData.type == LoginDataType.PIC_CAPTCHA) {
loginData.data
} else {
null
@RequestMapping("/login/v1", produces = ["application/x-protobuf"], consumes = ["application/x-protobuf"])
fun botLoginAsync(@RequestBody param: HttpDto.BotLoginAsyncReq): HttpDto.BotLoginAsyncResp {
GlobalScope.launch {
botService.botLogin(param.botId)
}
return HttpDto.BotLoginAsyncResp.newBuilder().build()
}

// 处理登陆
@RequestMapping("/solveLogin")
fun solveLogin(botId: Long, result: String): String {
myLoginSolver.solveLogin(botId, result)
return "ok"
}
}
28 changes: 28 additions & 0 deletions src/main/kotlin/net/lz1998/mirai/controller/CaptchaController.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package net.lz1998.mirai.controller

import dto.HttpDto
import net.lz1998.mirai.service.MyLoginSolver
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController

@RestController
@RequestMapping("/captcha")
class CaptchaController {

@Autowired
lateinit var myLoginSolver: MyLoginSolver

@RequestMapping("/list/v1", produces = ["application/x-protobuf"], consumes = ["application/x-protobuf"])
fun getCaptchaList(): HttpDto.GetCaptchaListResp {
val captchaList = myLoginSolver.getCaptchaList()
return HttpDto.GetCaptchaListResp.newBuilder().addAllCaptchaList(captchaList).build()
}

@RequestMapping("/solve/v1", produces = ["application/x-protobuf"], consumes = ["application/x-protobuf"])
fun solveCaptcha(@RequestBody param: HttpDto.SolveCaptchaReq): HttpDto.SolveCaptchaResp {
myLoginSolver.solveLogin(param.botId, param.result)
return HttpDto.SolveCaptchaResp.newBuilder().build()
}
}
20 changes: 20 additions & 0 deletions src/main/kotlin/net/lz1998/mirai/controller/CorsConf.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package net.lz1998.mirai.controller

import org.springframework.context.annotation.Configuration
import org.springframework.web.servlet.config.annotation.CorsRegistry
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer

@Configuration
class CorsConf : WebMvcConfigurer {
override fun addCorsMappings(registry: CorsRegistry) {
registry.addMapping("/**")
//是否发送Cookie
.allowCredentials(true)
//放行哪些原始域
.allowedOrigins("*")
.allowedMethods("GET", "POST", "PUT", "DELETE")
// .allowedHeaders("*")
// .exposedHeaders("*")

}
}
22 changes: 3 additions & 19 deletions src/main/kotlin/net/lz1998/mirai/entity/RemoteBot.kt
Original file line number Diff line number Diff line change
@@ -1,33 +1,17 @@
package net.lz1998.mirai.entity

import net.lz1998.mirai.ext.messageSourceLru
import net.lz1998.mirai.service.myLoginSolver
import net.mamoe.mirai.Bot
import net.mamoe.mirai.alsoLogin
import net.mamoe.mirai.event.events.BotEvent
import net.mamoe.mirai.event.subscribeAlways
import net.mamoe.mirai.message.MessageEvent
import onebot.OnebotFrame

interface RemoteBot {
var bot: Bot
var botId: Long
var password: String

suspend fun initBot() {
bot = Bot(botId, password) {
fileBasedDeviceInfo("device.json")
loginSolver = myLoginSolver
noNetworkLog()
}.alsoLogin()
bot.subscribeAlways<BotEvent> {
onBotEvent(this)
}
bot.subscribeAlways<MessageEvent> {
val messageSource = this.source // 撤回消息用
bot.messageSourceLru.put(messageSource.id, messageSource)
}
}
suspend fun initBot()

suspend fun login()

// 执行并返回结果
suspend fun onRemoteApi(req: OnebotFrame.Frame): OnebotFrame.Frame
Expand Down
Loading

0 comments on commit db12338

Please sign in to comment.