Review: misc improvements

This commit is contained in:
Him188 2020-03-05 09:45:54 +08:00
parent 7fa1e2631d
commit 62001cc1a8
13 changed files with 264 additions and 288 deletions

View File

@ -2,7 +2,7 @@
kotlin.code.style=official
# config
miraiVersion=0.24.1
mirai_console_version=0.3.1
miraiConsoleVersion=0.3.1
kotlin.incremental.multiplatform=true
kotlin.parallel.tasks.in.project=true
# kotlin

View File

@ -3,7 +3,6 @@ package net.mamoe.mirai.console.graphical.controller
import javafx.application.Platform
import javafx.collections.ObservableList
import javafx.stage.Modality
import kotlinx.io.core.IoBuffer
import net.mamoe.mirai.Bot
import net.mamoe.mirai.console.MiraiConsole
import net.mamoe.mirai.console.graphical.model.BotModel
@ -11,6 +10,7 @@ import net.mamoe.mirai.console.graphical.model.ConsoleInfo
import net.mamoe.mirai.console.graphical.model.PluginModel
import net.mamoe.mirai.console.graphical.model.VerificationCodeModel
import net.mamoe.mirai.console.graphical.view.VerificationCodeFragment
import net.mamoe.mirai.console.plugins.PluginManager
import net.mamoe.mirai.console.utils.MiraiConsoleUI
import net.mamoe.mirai.utils.LoginSolver
import tornadofx.*
@ -75,7 +75,7 @@ class MiraiGraphicalUIController : Controller(), MiraiConsoleUI {
override fun createLoginSolver(): LoginSolver = loginSolver
private fun getPluginsFromConsole(): ObservableList<PluginModel> =
MiraiConsole.pluginManager.getAllPluginDescriptions().map(::PluginModel).toObservable()
PluginManager.getAllPluginDescriptions().map(::PluginModel).toObservable()
}

View File

@ -1,3 +1,5 @@
@file:Suppress("MemberVisibilityCanBePrivate")
package net.mamoe.mirai.console
import com.googlecode.lanterna.SGR
@ -8,7 +10,6 @@ import com.googlecode.lanterna.input.KeyStroke
import com.googlecode.lanterna.input.KeyType
import com.googlecode.lanterna.terminal.DefaultTerminalFactory
import com.googlecode.lanterna.terminal.Terminal
import com.googlecode.lanterna.terminal.TerminalResizeListener
import com.googlecode.lanterna.terminal.swing.SwingTerminal
import com.googlecode.lanterna.terminal.swing.SwingTerminalFrame
import kotlinx.coroutines.*
@ -16,7 +17,6 @@ import kotlinx.coroutines.io.ByteWriteChannel
import kotlinx.coroutines.io.close
import kotlinx.coroutines.io.jvm.nio.copyTo
import kotlinx.coroutines.io.reader
import kotlinx.io.core.IoBuffer
import kotlinx.io.core.use
import net.mamoe.mirai.Bot
import net.mamoe.mirai.console.MiraiConsoleTerminalUI.LoggerDrawer.cleanPage
@ -51,49 +51,26 @@ import kotlin.system.exitProcess
*
*/
fun String.actualLength(): Int {
var x = 0
this.forEach {
if (it.isChineseChar()) {
x += 2
} else {
x += 1
}
}
return x
}
val String.actualLength: Int get() = this.sumBy { if (it.isChineseChar) 2 else 1 }
fun String.getSubStringIndexByActualLength(widthMax: Int): Int {
var index = 0
var currentLength = 0
this.forEach {
if (it.isChineseChar()) {
currentLength += 2
} else {
currentLength += 1
}
if (currentLength > widthMax) {
return@forEach
}
++index
}
if (index < 2) {
index = 2
}
return index
return this.sumBy { if (it.isChineseChar) 2 else 1 }.coerceAtMost(widthMax).coerceAtLeast(2)
}
fun Char.isChineseChar(): Boolean {
return this.toString().isChineseChar()
}
val Char.isChineseChar: Boolean
get() {
return this.toString().isChineseChar
}
fun String.isChineseChar(): Boolean {
return this.matches(Regex("[\u4e00-\u9fa5]"))
}
val String.isChineseChar: Boolean
get() {
return this.matches(Regex("[\u4e00-\u9fa5]"))
}
object MiraiConsoleTerminalUI : MiraiConsoleUI {
val cacheLogSize = 50
const val cacheLogSize = 50
var mainTitle = "Mirai Console v0.01 Core v0.15"
override fun pushVersion(consoleVersion: String, consoleBuild: String, coreVersion: String) {
@ -122,7 +99,7 @@ object MiraiConsoleTerminalUI : MiraiConsoleUI {
@Volatile
var requesting = false
var requestResult: String? = null
private var requestResult: String? = null
override suspend fun requestInput(): String {
requesting = true
while (requesting) {
@ -132,7 +109,7 @@ object MiraiConsoleTerminalUI : MiraiConsoleUI {
}
suspend fun provideInput(input: String) {
private suspend fun provideInput(input: String) {
if (requesting) {
requestResult = input
requesting = false
@ -164,17 +141,13 @@ object MiraiConsoleTerminalUI : MiraiConsoleUI {
}
}
var toLog = ""
lateinit var toLog: String
tempFile.inputStream().use {
val img = ImageIO.read(it)
if (img == null) {
toLog += "无法创建字符图片. 请查看文件\n"
} else {
toLog += img.createCharImg((terminal.terminalSize.columns / 1.5).toInt())
}
toLog += img?.createCharImg((terminal.terminalSize.columns / 1.5).toInt()) ?: "无法创建字符图片. 请查看文件\n"
}
pushLog(0, "$toLog[Login Solver]请输验证码. ${tempFile.absolutePath}")
return requestInput()!!
return requestInput()
.takeUnless { it.isEmpty() || it.length != 4 }
.also {
pushLog(0, "[Login Solver]正在提交[$it]中...")
@ -206,11 +179,11 @@ object MiraiConsoleTerminalUI : MiraiConsoleUI {
}
}
val log = ConcurrentHashMap<Long, LimitLinkedQueue<String>>().also {
private val log = ConcurrentHashMap<Long, LimitLinkedQueue<String>>().also {
it[0L] = LimitLinkedQueue(cacheLogSize)
}
val botAdminCount = ConcurrentHashMap<Long, Int>()
private val botAdminCount = ConcurrentHashMap<Long, Int>()
private val screens = mutableListOf(0L)
private var currentScreenId = 0
@ -219,7 +192,7 @@ object MiraiConsoleTerminalUI : MiraiConsoleUI {
lateinit var terminal: Terminal
lateinit var textGraphics: TextGraphics
var hasStart = false
private var hasStart = false
private lateinit var internalPrinter: PrintStream
fun start() {
if (hasStart) {
@ -277,11 +250,12 @@ object MiraiConsoleTerminalUI : MiraiConsoleUI {
*/
var lastJob: Job? = null
terminal.addResizeListener(TerminalResizeListener { terminal1: Terminal, newSize: TerminalSize ->
terminal.addResizeListener { _: Terminal, _: TerminalSize ->
lastJob = GlobalScope.launch {
try {
delay(300)
if (lastJob == coroutineContext[Job]) {
@Suppress("BlockingMethodInNonBlockingContext")
terminal.clearScreen()
//inited = false
update()
@ -292,7 +266,7 @@ object MiraiConsoleTerminalUI : MiraiConsoleUI {
pushLog(0, "[UI ERROR] ${e.message}")
}
}
})
}
if (terminal !is SwingTerminalFrame) {
System.setOut(PrintStream(object : OutputStream() {
@ -322,7 +296,7 @@ object MiraiConsoleTerminalUI : MiraiConsoleUI {
thread {
while (true) {
try {
var keyStroke: KeyStroke = terminal.readInput()
val keyStroke: KeyStroke = terminal.readInput()
when (keyStroke.keyType) {
KeyType.ArrowLeft -> {
@ -411,7 +385,7 @@ object MiraiConsoleTerminalUI : MiraiConsoleUI {
textGraphics.foregroundColor = TextColor.ANSI.WHITE
textGraphics.backgroundColor = TextColor.ANSI.GREEN
textGraphics.putString((width - mainTitle.actualLength()) / 2, 1, mainTitle, SGR.BOLD)
textGraphics.putString((width - mainTitle.actualLength) / 2, 1, mainTitle, SGR.BOLD)
textGraphics.foregroundColor = TextColor.ANSI.DEFAULT
textGraphics.backgroundColor = TextColor.ANSI.DEFAULT
textGraphics.putString(2, 3, "-".repeat(width - 4))
@ -426,15 +400,15 @@ object MiraiConsoleTerminalUI : MiraiConsoleUI {
val leftName =
getScreenName(getLeftScreenId())
// clearRows(2)
textGraphics.putString((width - title.actualLength()) / 2 - "$leftName << ".length, 2, "$leftName << ")
textGraphics.putString((width - title.actualLength) / 2 - "$leftName << ".length, 2, "$leftName << ")
textGraphics.foregroundColor = TextColor.ANSI.WHITE
textGraphics.backgroundColor = TextColor.ANSI.YELLOW
textGraphics.putString((width - title.actualLength()) / 2, 2, title, SGR.BOLD)
textGraphics.putString((width - title.actualLength) / 2, 2, title, SGR.BOLD)
textGraphics.foregroundColor = TextColor.ANSI.DEFAULT
textGraphics.backgroundColor = TextColor.ANSI.DEFAULT
val rightName =
getScreenName(getRightScreenId())
textGraphics.putString((width + title.actualLength()) / 2 + 1, 2, ">> $rightName")
textGraphics.putString((width + title.actualLength) / 2 + 1, 2, ">> $rightName")
}
fun drawMainFrame(
@ -447,7 +421,7 @@ object MiraiConsoleTerminalUI : MiraiConsoleUI {
clearRows(4)
textGraphics.putString(2, 4, "|Online Bots: $onlineBotCount")
textGraphics.putString(
width - 2 - "Powered By Mamoe Technologies|".actualLength(),
width - 2 - "Powered By Mamoe Technologies|".actualLength,
4,
"Powered By Mamoe Technologies|"
)
@ -463,7 +437,7 @@ object MiraiConsoleTerminalUI : MiraiConsoleUI {
textGraphics.backgroundColor = TextColor.ANSI.DEFAULT
clearRows(4)
textGraphics.putString(2, 4, "|Admins: $adminCount")
textGraphics.putString(width - 2 - "Add admins via commands|".actualLength(), 4, "Add admins via commands|")
textGraphics.putString(width - 2 - "Add admins via commands|".actualLength, 4, "Add admins via commands|")
}
@ -472,7 +446,7 @@ object MiraiConsoleTerminalUI : MiraiConsoleUI {
fun drawLog(string: String, flush: Boolean = true) {
val maxHeight = terminal.terminalSize.rows - 4
val heightNeed = (string.actualLength() / (terminal.terminalSize.columns - 6)) + 1
val heightNeed = (string.actualLength / (terminal.terminalSize.columns - 6)) + 1
if (heightNeed - 1 > maxHeight) {
pushLog(0, "[UI ERROR]: 您的屏幕太小, 有一条超长LOG无法显示")
return//拒绝打印
@ -481,7 +455,7 @@ object MiraiConsoleTerminalUI : MiraiConsoleUI {
cleanPage()//翻页
}
if (string.contains("\n")) {
string.split("\n").forEach {
string.split("\n").forEach { _ ->
drawLog(string, false)
}
} else {
@ -491,7 +465,7 @@ object MiraiConsoleTerminalUI : MiraiConsoleUI {
if (x == "") {
break
}
val toWrite = if (x.actualLength() > width) {
val toWrite = if (x.actualLength > width) {
val index = x.getSubStringIndexByActualLength(width)
x.substring(0, index).also {
x = if (index < x.length) {
@ -539,7 +513,7 @@ object MiraiConsoleTerminalUI : MiraiConsoleUI {
var vara = 0
val toPrint = mutableListOf<String>()
toDraw.forEach {
val heightNeed = (it.actualLength() / (terminal.terminalSize.columns - 6)) + 1
val heightNeed = (it.actualLength / (terminal.terminalSize.columns - 6)) + 1
vara += heightNeed
if (currentHeight + vara < terminal.terminalSize.rows - 4) {
logsToDraw++
@ -558,8 +532,8 @@ object MiraiConsoleTerminalUI : MiraiConsoleUI {
}
var commandBuilder = StringBuilder()
fun redrawCommand() {
private var commandBuilder = StringBuilder()
private fun redrawCommand() {
val height = terminal.terminalSize.rows
val width = terminal.terminalSize.columns
clearRows(height - 3)
@ -594,7 +568,7 @@ object MiraiConsoleTerminalUI : MiraiConsoleUI {
}
private fun deleteCommandChar() {
if (!commandBuilder.isEmpty()) {
if (commandBuilder.isNotEmpty()) {
commandBuilder = StringBuilder(commandBuilder.toString().substring(0, commandBuilder.length - 1))
}
val height = terminal.terminalSize.rows
@ -606,7 +580,7 @@ object MiraiConsoleTerminalUI : MiraiConsoleUI {
}
var lastEmpty: Job? = null
private var lastEmpty: Job? = null
private fun emptyCommand() {
commandBuilder = StringBuilder()
if (terminal is SwingTerminal) {
@ -617,7 +591,9 @@ object MiraiConsoleTerminalUI : MiraiConsoleUI {
try {
delay(100)
if (lastEmpty == coroutineContext[Job]) {
terminal.clearScreen()
withContext(Dispatchers.IO) {
terminal.clearScreen()
}
//inited = false
update()
redrawCommand()
@ -630,7 +606,7 @@ object MiraiConsoleTerminalUI : MiraiConsoleUI {
}
}
fun update() {
private fun update() {
when (screens[currentScreenId]) {
0L -> {
drawMainFrame(screens.size - 1)
@ -658,7 +634,7 @@ object MiraiConsoleTerminalUI : MiraiConsoleUI {
class LimitLinkedQueue<T>(
val limit: Int = 50
private val limit: Int = 50
) : ConcurrentLinkedDeque<T>() {
override fun push(e: T) {
if (size >= limit) {

View File

@ -26,7 +26,7 @@ fun kotlinx(id: String, version: String) = "org.jetbrains.kotlinx:kotlinx-$id:$v
fun ktor(id: String, version: String) = "io.ktor:ktor-$id:$version"
tasks.withType<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar>() {
tasks.withType<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar> {
manifest {
attributes["Main-Class"] = "net.mamoe.mirai.console.pure.MiraiConsolePureLoader"
}
@ -35,6 +35,16 @@ tasks.withType<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar>() {
val miraiVersion: String by rootProject.ext
kotlin {
sourceSets {
all {
languageSettings.enableLanguageFeature("InlineClasses")
languageSettings.useExperimentalAnnotation("kotlin.Experimental")
languageSettings.useExperimentalAnnotation("kotlin.OptIn")
}
}
}
dependencies {
compileOnly("net.mamoe:mirai-core-jvm:$miraiVersion")
// compileOnly("net.mamoe:mirai-core-qqandroid-jvm:$miraiVersion")
@ -65,8 +75,8 @@ dependencies {
api(ktor("network", ktorVersion))
}
val mirai_console_version: String by project.ext
version = mirai_console_version
val miraiConsoleVersion: String by project.ext
version = miraiConsoleVersion
description = "Console with plugin support for mirai"
bintray {
@ -109,7 +119,7 @@ publishing {
groupId = rootProject.group.toString()
artifactId = "mirai-console"
version = mirai_console_version
version = miraiConsoleVersion
pom.withXml {
val root = asNode()

View File

@ -12,7 +12,6 @@ package net.mamoe.mirai.console
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.Channel
import net.mamoe.mirai.Bot
import net.mamoe.mirai.BotFactory
import net.mamoe.mirai.console.MiraiConsole.CommandProcessor.processNextCommandLine
import net.mamoe.mirai.console.command.CommandManager
import net.mamoe.mirai.console.command.CommandSender
@ -38,20 +37,10 @@ object MiraiConsole {
* */
val bots get() = Bot.instances
fun getBotByUIN(uin: Long): Bot? {
bots.forEach {
if (it.get()?.uin == uin) {
return it.get()
}
}
return null
fun getBotOrNull(uin: Long): Bot? {
return bots.asSequence().mapNotNull { it.get() }.firstOrNull { it.uin == uin }
}
/**
* PluginManager
*/
val pluginManager: PluginManager get() = PluginManager
/**
* 与前端交互所使用的Logger
*/
@ -89,9 +78,8 @@ object MiraiConsole {
logger("Mirai为开源项目请自觉遵守开源项目协议")
logger("Powered by Mamoe Technologies and contributors")
MiraiCoreLoader()
MiraiCoreLoader.loadCore()
println(BotFactory::class)
/* 加载ECDH */
try {
ECDH()
@ -102,7 +90,7 @@ object MiraiConsole {
/* 依次启用功能 */
DefaultCommands()
pluginManager.loadPlugins()
PluginManager.loadPlugins()
CommandProcessor.start()
/* 通知启动完成 */
@ -150,6 +138,7 @@ object MiraiConsole {
fun runConsoleCommandBlocking(command: String) = runBlocking { runConsoleCommand(command) }
@Suppress("unused")
fun runCommandBlocking(sender: CommandSender, command: String) = runBlocking { runCommand(sender, command) }
private suspend fun processNextCommandLine() {

View File

@ -7,82 +7,18 @@
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
@file:Suppress("NOTHING_TO_INLINE")
package net.mamoe.mirai.console.command
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import net.mamoe.mirai.console.MiraiConsole
import net.mamoe.mirai.console.plugins.PluginManager
import net.mamoe.mirai.console.plugins.PluginBase
import net.mamoe.mirai.contact.Contact
import net.mamoe.mirai.contact.sendMessage
import net.mamoe.mirai.message.GroupMessage
import net.mamoe.mirai.message.data.MessageChain
import net.mamoe.mirai.utils.MiraiExperimentalAPI
object CommandManager {
private val registeredCommand: MutableMap<String, Command> = mutableMapOf()
fun getCommands(): Collection<Command> {
return registeredCommand.values
}
fun register(command: Command) {
val allNames = mutableListOf(command.name).also { it.addAll(command.alias) }
allNames.forEach {
if (registeredCommand.containsKey(it)) {
error("Command Name(or Alias) $it is already registered, consider if same functional plugin was installed")
}
}
allNames.forEach {
registeredCommand[it] = command
}
}
fun unregister(command: Command) {
val allNames = mutableListOf<String>(command.name).also { it.addAll(command.alias) }
allNames.forEach {
registeredCommand.remove(it)
}
}
fun unregister(commandName: String) {
registeredCommand.remove(commandName)
}
/*
* Index: MiraiConsole
* */
internal suspend fun runCommand(sender: CommandSender, fullCommand: String): Boolean {
val blocks = fullCommand.split(" ")
val commandHead = blocks[0].replace("/", "")
if (!registeredCommand.containsKey(commandHead)) {
return false
}
val args = blocks.subList(1, blocks.size)
registeredCommand[commandHead]?.run {
try {
if (onCommand(
sender,
blocks.subList(1, blocks.size)
)
) {
PluginManager.onCommand(this, sender, args)
} else {
sender.sendMessage(this.usage)
}
} catch (e: Exception) {
sender.sendMessage("在运行指令时出现了未知错误")
e.printStackTrace()
} finally {
(sender as CommandSenderImpl).flushMessage()
}
}
return true
}
}
interface CommandSender {
/**
@ -92,7 +28,7 @@ interface CommandSender {
suspend fun sendMessage(message: String)
/**
* 写入要发送的内容 所有内容最后会被以一条发出, 不管成功与否
* 写入要发送的内容 所有内容最后会被以一条发出
*/
fun appendMessage(message: String)
@ -139,21 +75,6 @@ open class ContactCommandSender(val contact: Contact) : CommandSenderImpl() {
}
}
/**
* 弃用中
* */
class GroupCommandSender(val toQuote: GroupMessage, contact: Contact) : ContactCommandSender(contact) {
@MiraiExperimentalAPI
override suspend fun sendMessage(message: String) {
toQuote.quoteReply(message)
}
@MiraiExperimentalAPI
override suspend fun sendMessage(messageChain: MessageChain) {
toQuote.quoteReply(messageChain)
}
}
interface Command {
val name: String
val alias: List<String>
@ -161,9 +82,18 @@ interface Command {
val usage: String
suspend fun onCommand(sender: CommandSender, args: List<String>): Boolean
fun register()
}
inline fun Command.register() = CommandManager.register(this)
fun registerCommand(builder: CommandBuilder.() -> Unit): Command {
return CommandBuilder().apply(builder).register()
}
// for java
@Suppress("unused")
abstract class BlockingCommand(
override val name: String,
override val alias: List<String> = listOf(),
@ -172,7 +102,7 @@ abstract class BlockingCommand(
) : Command {
/**
* 最高优先级监听器
* 如果 return `false` 这次指令不会被 [PluginBase] 的全局 onCommand 监听器监听
* 如果 return `false`, 这次指令不会被 [PluginBase] 的全局 onCommand 监听器监听
* */
final override suspend fun onCommand(sender: CommandSender, args: List<String>): Boolean {
return withContext(Dispatchers.IO) {
@ -181,10 +111,6 @@ abstract class BlockingCommand(
}
abstract fun onCommandBlocking(sender: CommandSender, args: List<String>): Boolean
override fun register() {
CommandManager.register(this)
}
}
class AnonymousCommand internal constructor(
@ -197,10 +123,6 @@ class AnonymousCommand internal constructor(
override suspend fun onCommand(sender: CommandSender, args: List<String>): Boolean {
return onCommand.invoke(sender, args)
}
override fun register() {
CommandManager.register(this)
}
}
class CommandBuilder internal constructor() {
@ -208,30 +130,25 @@ class CommandBuilder internal constructor() {
var alias: List<String>? = null
var description: String = ""
var usage: String = "use /help for help"
var onCommand: (suspend CommandSender.(args: List<String>) -> Boolean)? = null
internal var onCommand: (suspend CommandSender.(args: List<String>) -> Boolean)? = null
fun onCommand(commandProcess: suspend CommandSender.(args: List<String>) -> Boolean) {
onCommand = commandProcess
}
}
fun register(): Command {
if (name == null || onCommand == null) {
error("CommandBuilder not complete")
}
if (alias == null) {
alias = listOf()
}
return AnonymousCommand(
name!!,
alias!!,
description,
usage,
onCommand!!
).also { it.register() }
private fun CommandBuilder.register(): AnonymousCommand {
if (name == null || onCommand == null) {
error("CommandBuilder not complete")
}
}
fun registerCommand(builder: CommandBuilder.() -> Unit): Command {
return CommandBuilder().apply(builder).register()
}
if (alias == null) {
alias = listOf()
}
return AnonymousCommand(
name!!,
alias!!,
description,
usage,
onCommand!!
).also { it.register() }
}

View File

@ -0,0 +1,70 @@
/*
* Copyright 2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
@file:Suppress("unused")
package net.mamoe.mirai.console.command
import net.mamoe.mirai.console.plugins.PluginManager
object CommandManager {
private val registeredCommand: MutableMap<String, Command> = mutableMapOf()
val commands: Collection<Command> get() = registeredCommand.values
fun register(command: Command) {
val allNames = mutableListOf(command.name).also { it.addAll(command.alias) }
allNames.forEach {
if (registeredCommand.containsKey(it)) {
error("Command Name(or Alias) $it is already registered, consider if same functional plugin was installed")
}
}
allNames.forEach {
registeredCommand[it] = command
}
}
fun unregister(command: Command) {
(command.alias.asSequence() + command.name).forEach {
registeredCommand.remove(it)
} // label compilation failed
}
fun unregister(commandName: String): Boolean {
return registeredCommand.remove(commandName) != null
}
/*
* Index: MiraiConsole
*/
internal suspend fun runCommand(sender: CommandSender, fullCommand: String): Boolean {
val blocks = fullCommand.split(" ")
val commandHead = blocks[0].replace("/", "")
if (!registeredCommand.containsKey(commandHead)) {
return false
}
val args = blocks.drop(1)
registeredCommand[commandHead]?.run {
try {
if (onCommand(sender, blocks.drop(1))) {
PluginManager.onCommand(this, sender, args)
} else {
sender.sendMessage(this.usage)
}
} catch (e: Exception) {
sender.sendMessage("在运行指令时出现了未知错误")
e.printStackTrace()
} finally {
(sender as CommandSenderImpl).flushMessage()
}
}
return true
}
}

View File

@ -14,7 +14,7 @@ import net.mamoe.mirai.console.MiraiConsole
import net.mamoe.mirai.console.plugins.PluginManager
import net.mamoe.mirai.console.utils.addManager
import net.mamoe.mirai.console.utils.checkManager
import net.mamoe.mirai.console.utils.getManagers
import net.mamoe.mirai.console.utils.managers
import net.mamoe.mirai.console.utils.removeManager
import net.mamoe.mirai.contact.sendMessage
import net.mamoe.mirai.event.subscribeMessages
@ -49,7 +49,7 @@ object DefaultCommands {
MiraiConsole.logger("[Bot Manager]", 0, it[1] + " 不是一个Bot的ID")
return@onCommand false
}
val bot = MiraiConsole.getBotByUIN(botId)
val bot = MiraiConsole.getBotOrNull(botId)
if (bot == null) {
MiraiConsole.logger("[Bot Manager]", 0, "$botId 没有在Console中登陆")
return@onCommand false
@ -88,7 +88,7 @@ object DefaultCommands {
MiraiConsole.logger("[Bot Manager]", 0, it[2] + "移除成功")
}
"list" -> {
bot.getManagers().forEach {
bot.managers.forEach {
MiraiConsole.logger("[Bot Manager]", 0, " -> $it")
}
}
@ -106,7 +106,7 @@ object DefaultCommands {
return@onCommand false
}
if (it.size < 2) {
MiraiConsole.logger("\"/login qqnumber qqpassword \" to login a bot")
MiraiConsole.logger("\"/login qq password \" to login a bot")
MiraiConsole.logger("\"/login qq号 qq密码 \" 来登录一个BOT")
return@onCommand false
}
@ -138,11 +138,11 @@ object DefaultCommands {
}
bot.login()
bot.subscribeMessages {
this.startsWith("/") {
startsWith("/") { message ->
if (bot.checkManager(this.sender.id)) {
val sender = ContactCommandSender(this.subject)
MiraiConsole.CommandProcessor.runCommand(
sender, it
sender, message
)
}
}
@ -159,13 +159,13 @@ object DefaultCommands {
registerCommand {
name = "status"
description = "获取状态"
onCommand {
when (it.size) {
onCommand { args ->
when (args.size) {
0 -> {
sendMessage("当前有" + MiraiConsole.bots.size + "个BOT在线")
}
1 -> {
val bot = it[0]
val bot = args[0]
var find = false
MiraiConsole.bots.forEach {
if (it.get()?.uin.toString().contains(bot)) {
@ -196,13 +196,13 @@ object DefaultCommands {
return@onCommand false
}
val bot: Bot? = if (it.size == 2) {
if (MiraiConsole.bots.size == 0) {
if (MiraiConsole.bots.isEmpty()) {
MiraiConsole.logger("还没有BOT登录")
return@onCommand false
}
MiraiConsole.bots[0].get()
} else {
MiraiConsole.getBotByUIN(it[0].toLong())
MiraiConsole.getBotOrNull(it[0].toLong())
}
if (bot == null) {
MiraiConsole.logger("没有找到BOT")
@ -228,11 +228,11 @@ object DefaultCommands {
alias = listOf("plugin")
description = "获取插件列表"
onCommand {
PluginManager.getAllPluginDescriptions().let {
it.forEach {
PluginManager.getAllPluginDescriptions().let { descriptions ->
descriptions.forEach {
appendMessage("\t" + it.name + " v" + it.version + " by" + it.author + " " + it.info)
}
appendMessage("加载了" + it.size + "个插件")
appendMessage("加载了" + descriptions.size + "个插件")
true
}
}
@ -243,10 +243,10 @@ object DefaultCommands {
alias = listOf("commands", "help", "helps")
description = "获取指令列表"
onCommand {
CommandManager.getCommands().let {
CommandManager.commands.let { commands ->
var size = 0
appendMessage("")//\n
it.toSet().forEach {
commands.forEach {
++size
appendMessage("-> " + it.name + " :" + it.description)
}

View File

@ -7,15 +7,15 @@
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
@file:Suppress("EXPERIMENTAL_API_USAGE")
package net.mamoe.mirai.console.core
import io.ktor.client.HttpClient
import io.ktor.client.engine.cio.CIO
import io.ktor.client.request.get
import io.ktor.client.request.url
import io.ktor.client.statement.HttpResponse
import io.ktor.http.URLProtocol
import io.ktor.util.KtorExperimentalAPI
import io.ktor.utils.io.ByteReadChannel
import io.ktor.utils.io.jvm.javaio.copyTo
import kotlinx.coroutines.Dispatchers
@ -27,12 +27,11 @@ import java.net.URLClassLoader
import kotlin.math.pow
import kotlin.system.exitProcess
@UseExperimental(KtorExperimentalAPI::class)
val Http: HttpClient
get() = HttpClient(CIO)
object MiraiCoreLoader {
val coresPath by lazy {
private val coresPath by lazy {
File(System.getProperty("user.dir") + "/core/").also {
if (!it.exists()) {
it.mkdirs()
@ -59,7 +58,7 @@ object MiraiCoreLoader {
}
operator fun invoke(): String {
fun loadCore(): String {
MiraiConsole.logger("Fetching Newest Core Version .. ")
val newest = runBlocking {
getNewestVersion()
@ -82,7 +81,7 @@ object MiraiCoreLoader {
/**
* 使用Protocol Lib判断最新版本
* 判断最新版本
* */
private suspend fun getNewestVersion(): String {
try {
@ -110,9 +109,9 @@ object MiraiCoreLoader {
}
/**
* 使用Protocol Lib判断当前版本
* 如果没有 会返回0.0.0
* */
* 判断当前版本
* 默认返回 "0.0.0"
*/
private fun getCurrentVersion(): String {
val file = getProtocolLib()
if (file == null || getCore() == null) return "0.0.0"
@ -133,14 +132,18 @@ object MiraiCoreLoader {
}
val lib_jcenter =
"https://jcenter.bintray.com/net/mamoe/mirai-core-qqandroid-jvm/{version}/:mirai-core-qqandroid-jvm-{version}.jar"
val lib_aliyun =
"https://maven.aliyun.com/nexus/content/repositories/jcenter/net/mamoe/mirai-core-qqandroid-jvm/{version}/mirai-core-qqandroid-jvm-{version}.jar"
@Suppress("SpellCheckingInspection")
private object Links {
internal const val libJcenter =
"https://jcenter.bintray.com/net/mamoe/mirai-core-qqandroid-jvm/{version}/:mirai-core-qqandroid-jvm-{version}.jar"
internal const val libAliyun =
"https://maven.aliyun.com/nexus/content/repositories/jcenter/net/mamoe/mirai-core-qqandroid-jvm/{version}/mirai-core-qqandroid-jvm-{version}.jar"
val core_jcenter = "https://jcenter.bintray.com/net/mamoe/mirai-core-jvm/{version}/:mirai-core-jvm-{version}.jar"
val core_aliyun =
"https://maven.aliyun.com/nexus/content/repositories/jcenter/net/mamoe/mirai-core-jvm/{version}/mirai-core-jvm-{version}.jar"
internal const val coreJcenter =
"https://jcenter.bintray.com/net/mamoe/mirai-core-jvm/{version}/:mirai-core-jvm-{version}.jar"
internal const val coreAliyun =
"https://maven.aliyun.com/nexus/content/repositories/jcenter/net/mamoe/mirai-core-jvm/{version}/mirai-core-jvm-{version}.jar"
}
private suspend fun downloadCoreAndLib(version: String) {
var fileStream = File(coresPath.absolutePath + "/" + "mirai-core-qqandroid-jvm-$version.jar").also {
@ -150,18 +153,16 @@ object MiraiCoreLoader {
}.outputStream()
suspend fun downloadRequest(url: String, version: String): ByteReadChannel {
return Http.get<HttpResponse>() {
this.url(url.replace("{version}", version))
}.content
return Http.get<HttpResponse>(url.replace("{version}", version)).content
}
var stream = kotlin.runCatching {
MiraiConsole.logger("Downloading newest Protocol lib from Aliyun")
downloadRequest(lib_aliyun, version)
downloadRequest(Links.libAliyun, version)
}.getOrElse {
kotlin.runCatching {
MiraiConsole.logger("Downloading newest Protocol lib from JCenter")
downloadRequest(lib_jcenter, version)
downloadRequest(Links.libJcenter, version)
}.getOrElse { e ->
MiraiConsole.logger("Failed to download Protocol lib, please seeking for help")
e.printStackTrace()
@ -184,11 +185,11 @@ object MiraiCoreLoader {
stream = try {
MiraiConsole.logger("Downloading newest Mirai Core from Aliyun")
downloadRequest(core_aliyun, version)
downloadRequest(Links.coreAliyun, version)
} catch (ignored: Exception) {
try {
MiraiConsole.logger("Downloading newest Mirai Core from JCenter")
downloadRequest(core_jcenter, version)
downloadRequest(Links.coreJcenter, version)
} catch (e: Exception) {
MiraiConsole.logger("Failed to download Mirai Core, please seeking for help")
e.printStackTrace()
@ -240,8 +241,5 @@ object MiraiCoreLoader {
}
internal class MiraiCoreClassLoader(file: File, parent: ClassLoader) :
URLClassLoader(arrayOf(file.toURI().toURL()), parent)

View File

@ -7,6 +7,8 @@
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
@file:Suppress("MemberVisibilityCanBePrivate")
package net.mamoe.mirai.console.plugins
import com.alibaba.fastjson.JSON
@ -17,12 +19,14 @@ import com.moandjiezana.toml.Toml
import com.moandjiezana.toml.TomlWriter
import kotlinx.serialization.Serializable
import kotlinx.serialization.UnstableDefault
import net.mamoe.mirai.utils.MiraiInternalAPI
import net.mamoe.mirai.utils.io.encodeToString
import org.yaml.snakeyaml.Yaml
import java.io.File
import java.io.InputStream
import java.util.*
import java.util.concurrent.ConcurrentHashMap
import kotlin.NoSuchElementException
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KClass
import kotlin.reflect.KProperty
@ -134,16 +138,16 @@ inline operator fun <reified T : Any> Config.setValue(thisRef: Any?, property: K
}
/* 带有默认值的代理 */
@Suppress("unused")
inline fun <reified T : Any> Config.withDefault(
noinline defaultValue: () -> T
crossinline defaultValue: () -> T
): ReadWriteProperty<Any, T> {
val default by lazy { defaultValue.invoke() }
return object : ReadWriteProperty<Any, T> {
override fun getValue(thisRef: Any, property: KProperty<*>): T {
if (this@withDefault.exist(property.name)) {//unsafe
return this@withDefault.smartCast(property)
}
return default
return defaultValue()
}
override fun setValue(thisRef: Any, property: KProperty<*>, value: T) {
@ -153,6 +157,7 @@ inline fun <reified T : Any> Config.withDefault(
}
/* 带有默认值且如果为空会写入的代理 */
@Suppress("unused")
inline fun <reified T : Any> Config.withDefaultWrite(
noinline defaultValue: () -> T
): WithDefaultWriteLoader<T> {
@ -191,7 +196,7 @@ class WithDefaultWriteLoader<T : Any>(
return object : ReadWriteProperty<Any, T> {
override fun getValue(thisRef: Any, property: KProperty<*>): T {
if (config.exist(property.name)) {//unsafe
return config._smartCast(property.name, _class)
return config.smartCastInternal(property.name, _class)
}
return defaultValue
}
@ -203,12 +208,14 @@ class WithDefaultWriteLoader<T : Any>(
}
}
inline fun <reified T : Any> Config.smartCast(property: KProperty<*>): T {
return _smartCast(property.name, T::class)
@PublishedApi
internal inline fun <reified T : Any> Config.smartCast(property: KProperty<*>): T {
return smartCastInternal(property.name, T::class)
}
@PublishedApi
@Suppress("IMPLICIT_CAST_TO_ANY", "UNCHECKED_CAST")
fun <T : Any> Config._smartCast(propertyName: String, _class: KClass<T>): T {
internal fun <T : Any> Config.smartCastInternal(propertyName: String, _class: KClass<T>): T {
return when (_class) {
String::class -> this.getString(propertyName)
Int::class -> this.getInt(propertyName)
@ -251,70 +258,72 @@ fun <T : Any> Config._smartCast(propertyName: String, _class: KClass<T>): T {
interface ConfigSection : Config, MutableMap<String, Any> {
override fun getConfigSection(key: String): ConfigSection {
val content = get(key) ?: error("ConfigSection does not contain $key ")
val content = get(key) ?: throw NoSuchElementException(key)
if (content is ConfigSection) {
return content
}
@Suppress("UNCHECKED_CAST")
return ConfigSectionDelegation(
Collections.synchronizedMap(
(get(key) ?: error("ConfigSection does not contain $key ")) as LinkedHashMap<String, Any>
(get(key) ?: throw NoSuchElementException(key)) as LinkedHashMap<String, Any>
)
)
}
override fun getString(key: String): String {
return (get(key) ?: error("ConfigSection does not contain $key ")).toString()
return (get(key) ?: throw NoSuchElementException(key)).toString()
}
override fun getInt(key: String): Int {
return (get(key) ?: error("ConfigSection does not contain $key ")).toString().toInt()
return (get(key) ?: throw NoSuchElementException(key)).toString().toInt()
}
override fun getFloat(key: String): Float {
return (get(key) ?: error("ConfigSection does not contain $key ")).toString().toFloat()
return (get(key) ?: throw NoSuchElementException(key)).toString().toFloat()
}
override fun getBoolean(key: String): Boolean {
return (get(key) ?: error("ConfigSection does not contain $key ")).toString().toBoolean()
return (get(key) ?: throw NoSuchElementException(key)).toString().toBoolean()
}
override fun getDouble(key: String): Double {
return (get(key) ?: error("ConfigSection does not contain $key ")).toString().toDouble()
return (get(key) ?: throw NoSuchElementException(key)).toString().toDouble()
}
override fun getLong(key: String): Long {
return (get(key) ?: error("ConfigSection does not contain $key ")).toString().toLong()
return (get(key) ?: throw NoSuchElementException(key)).toString().toLong()
}
override fun getList(key: String): List<*> {
return ((get(key) ?: error("ConfigSection does not contain $key ")) as List<*>)
return ((get(key) ?: throw NoSuchElementException(key)) as List<*>)
}
override fun getStringList(key: String): List<String> {
return ((get(key) ?: error("ConfigSection does not contain $key ")) as List<*>).map { it.toString() }
return ((get(key) ?: throw NoSuchElementException(key)) as List<*>).map { it.toString() }
}
override fun getIntList(key: String): List<Int> {
return ((get(key) ?: error("ConfigSection does not contain $key ")) as List<*>).map { it.toString().toInt() }
return ((get(key) ?: throw NoSuchElementException(key)) as List<*>).map { it.toString().toInt() }
}
override fun getFloatList(key: String): List<Float> {
return ((get(key) ?: error("ConfigSection does not contain $key ")) as List<*>).map { it.toString().toFloat() }
return ((get(key) ?: throw NoSuchElementException(key)) as List<*>).map { it.toString().toFloat() }
}
override fun getDoubleList(key: String): List<Double> {
return ((get(key) ?: error("ConfigSection does not contain $key ")) as List<*>).map { it.toString().toDouble() }
return ((get(key) ?: throw NoSuchElementException(key)) as List<*>).map { it.toString().toDouble() }
}
override fun getLongList(key: String): List<Long> {
return ((get(key) ?: error("ConfigSection does not contain $key ")) as List<*>).map { it.toString().toLong() }
return ((get(key) ?: throw NoSuchElementException(key)) as List<*>).map { it.toString().toLong() }
}
override fun getConfigSectionList(key: String): List<ConfigSection> {
return ((get(key) ?: error("ConfigSection does not contain $key ")) as List<*>).map {
return ((get(key) ?: throw NoSuchElementException(key)) as List<*>).map {
if (it is ConfigSection) {
it
} else {
@Suppress("UNCHECKED_CAST")
ConfigSectionDelegation(
Collections.synchronizedMap(
it as MutableMap<String, Any>
@ -334,7 +343,7 @@ interface ConfigSection : Config, MutableMap<String, Any> {
}
@Serializable
open class ConfigSectionImpl() : ConcurrentHashMap<String, Any>(),
open class ConfigSectionImpl : ConcurrentHashMap<String, Any>(),
ConfigSection {
override fun set(key: String, value: Any) {
super.put(key, value)
@ -370,7 +379,7 @@ open class ConfigSectionDelegation(
private val delegate: MutableMap<String, Any>
) : ConfigSection, MutableMap<String, Any> by delegate {
override fun set(key: String, value: Any) {
delegate.put(key, value)
delegate[key] = value
}
override fun contains(key: String): Boolean {
@ -394,6 +403,7 @@ interface FileConfig : Config {
}
@MiraiInternalAPI
abstract class FileConfigImpl internal constructor(
private val rawContent: String
) : FileConfig,
@ -402,6 +412,7 @@ abstract class FileConfigImpl internal constructor(
internal var file: File? = null
@Suppress("unused")
constructor(file: File) : this(file.readText()) {
this.file = file
}
@ -425,7 +436,7 @@ abstract class FileConfigImpl internal constructor(
override fun remove(key: String): Any? = content.remove(key)
override fun save() {
if (isReadOnly()) {
if (isReadOnly) {
error("Config is readonly")
}
if (!((file?.exists())!!)) {
@ -434,7 +445,7 @@ abstract class FileConfigImpl internal constructor(
file?.writeText(serialize(content))
}
fun isReadOnly() = file == null
val isReadOnly: Boolean get() = file == null
override fun contains(key: String): Boolean {
return content.contains(key)
@ -454,6 +465,7 @@ abstract class FileConfigImpl internal constructor(
}
@UseExperimental(MiraiInternalAPI::class)
class JsonConfig internal constructor(
content: String
) : FileConfigImpl(content) {
@ -466,7 +478,7 @@ class JsonConfig internal constructor(
if (content.isEmpty() || content.isBlank() || content == "{}") {
return ConfigSectionImpl()
}
return JSON.parseObject<ConfigSectionImpl>(
return JSON.parseObject(
content,
object : TypeReference<ConfigSectionImpl>() {},
Feature.OrderedField
@ -479,6 +491,7 @@ class JsonConfig internal constructor(
}
}
@UseExperimental(MiraiInternalAPI::class)
class YamlConfig internal constructor(content: String) : FileConfigImpl(content) {
constructor(file: File) : this(file.readText()) {
this.file = file
@ -490,7 +503,7 @@ class YamlConfig internal constructor(content: String) : FileConfigImpl(content)
}
return ConfigSectionDelegation(
Collections.synchronizedMap(
Yaml().load<LinkedHashMap<String, Any>>(content) as LinkedHashMap<String, Any>
Yaml().load(content) as LinkedHashMap<String, Any>
)
)
}
@ -501,6 +514,7 @@ class YamlConfig internal constructor(content: String) : FileConfigImpl(content)
}
@UseExperimental(MiraiInternalAPI::class)
class TomlConfig internal constructor(content: String) : FileConfigImpl(content) {
constructor(file: File) : this(file.readText()) {
this.file = file

View File

@ -7,6 +7,8 @@
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
@file:Suppress("unused", "unused")
package net.mamoe.mirai.console.plugins
import kotlinx.coroutines.CancellationException
@ -22,7 +24,6 @@ import java.util.jar.JarFile
object PluginManager {
@Volatile
internal var lastPluginName: String = ""

View File

@ -18,8 +18,8 @@ import net.mamoe.mirai.utils.LoginSolver
import kotlin.concurrent.thread
class MiraiConsoleUIPure : MiraiConsoleUI {
var requesting = false
var requestStr = ""
private var requesting = false
private var requestStr = ""
init {
thread {

View File

@ -18,32 +18,33 @@ import net.mamoe.mirai.console.plugins.withDefaultWriteSave
import net.mamoe.mirai.console.utils.BotManagers.BOT_MANAGERS
import java.io.File
object BotManagers {
internal object BotManagers {
val config = File("${MiraiConsole.path}/bot.yml").loadAsConfig()
val BOT_MANAGERS: ConfigSection by config.withDefaultWriteSave { ConfigSectionImpl() }
}
fun Bot.addManager(long: Long) {
internal fun Bot.addManager(long: Long) {
BOT_MANAGERS.putIfAbsent(this.uin.toString(), mutableListOf<Long>())
BOT_MANAGERS[this.uin.toString()] =
(BOT_MANAGERS.getLongList(this.uin.toString()) as MutableList<Long>).apply { add(long) }
BotManagers.config.save()
}
fun Bot.removeManager(long: Long) {
internal fun Bot.removeManager(long: Long) {
BOT_MANAGERS.putIfAbsent(this.uin.toString(), mutableListOf<Long>())
BOT_MANAGERS[this.uin.toString()] =
(BOT_MANAGERS.getLongList(this.uin.toString()) as MutableList<Long>).apply { add(long) }
BotManagers.config.save()
}
fun Bot.getManagers(): List<Long> {
BOT_MANAGERS.putIfAbsent(this.uin.toString(), mutableListOf<Long>())
return BOT_MANAGERS.getLongList(this.uin.toString())
}
internal val Bot.managers: List<Long>
get() {
BOT_MANAGERS.putIfAbsent(this.uin.toString(), mutableListOf<Long>())
return BOT_MANAGERS.getLongList(this.uin.toString())
}
fun Bot.checkManager(long: Long): Boolean {
return this.getManagers().contains(long)
internal fun Bot.checkManager(long: Long): Boolean {
return this.managers.contains(long)
}