Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
M
Mirai
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Locked Files
Issues
0
Issues
0
List
Boards
Labels
Service Desk
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Security & Compliance
Security & Compliance
Dependency List
License Compliance
Packages
Packages
List
Container Registry
Analytics
Analytics
CI / CD
Code Review
Insights
Issues
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
MyCard
Mirai
Commits
c6ae8e32
Commit
c6ae8e32
authored
Feb 27, 2020
by
Him188
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Separate `mirai-console` series from main repository
parent
6110cf8f
Changes
35
Hide whitespace changes
Inline
Side-by-side
Showing
35 changed files
with
0 additions
and
3369 deletions
+0
-3369
mirai-console-graphical/README.md
mirai-console-graphical/README.md
+0
-6
mirai-console-graphical/build.gradle.kts
mirai-console-graphical/build.gradle.kts
+0
-45
mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/MiraiGraphical.kt
...otlin/net/mamoe/mirai/console/graphical/MiraiGraphical.kt
+0
-36
mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/controller/MiraiGraphicalUIController.kt
...onsole/graphical/controller/MiraiGraphicalUIController.kt
+0
-94
mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/model/BotModel.kt
...otlin/net/mamoe/mirai/console/graphical/model/BotModel.kt
+0
-19
mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/model/ConsoleInfo.kt
...in/net/mamoe/mirai/console/graphical/model/ConsoleInfo.kt
+0
-17
mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/model/PluginModel.kt
...in/net/mamoe/mirai/console/graphical/model/PluginModel.kt
+0
-20
mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/model/VerificationCodeModel.kt
...oe/mirai/console/graphical/model/VerificationCodeModel.kt
+0
-17
mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/styleSheet/LoginViewStyleSheet.kt
...mirai/console/graphical/styleSheet/LoginViewStyleSheet.kt
+0
-47
mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/styleSheet/PrimaryStyleSheet.kt
...e/mirai/console/graphical/styleSheet/PrimaryStyleSheet.kt
+0
-21
mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/util/JFoenixAdaptor.kt
.../net/mamoe/mirai/console/graphical/util/JFoenixAdaptor.kt
+0
-48
mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/view/Decorator.kt
...otlin/net/mamoe/mirai/console/graphical/view/Decorator.kt
+0
-9
mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/view/LoginView.kt
...otlin/net/mamoe/mirai/console/graphical/view/LoginView.kt
+0
-50
mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/view/PluginsView.kt
...lin/net/mamoe/mirai/console/graphical/view/PluginsView.kt
+0
-33
mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/view/PrimaryView.kt
...lin/net/mamoe/mirai/console/graphical/view/PrimaryView.kt
+0
-99
mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/view/SettingsView.kt
...in/net/mamoe/mirai/console/graphical/view/SettingsView.kt
+0
-36
mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/view/VerificationCodeFragment.kt
.../mirai/console/graphical/view/VerificationCodeFragment.kt
+0
-19
mirai-console-graphical/src/main/resources/character.png
mirai-console-graphical/src/main/resources/character.png
+0
-0
mirai-console-graphical/src/main/resources/logo.png
mirai-console-graphical/src/main/resources/logo.png
+0
-0
mirai-console-terminal/README.md
mirai-console-terminal/README.md
+0
-6
mirai-console-terminal/build.gradle.kts
mirai-console-terminal/build.gradle.kts
+0
-44
mirai-console-terminal/src/main/kotlin/net/mamoe/mirai/console/MiraiConsoleTerminalLoader.kt
...lin/net/mamoe/mirai/console/MiraiConsoleTerminalLoader.kt
+0
-31
mirai-console-terminal/src/main/kotlin/net/mamoe/mirai/console/MiraiConsoleTerminalUI.kt
.../kotlin/net/mamoe/mirai/console/MiraiConsoleTerminalUI.kt
+0
-662
mirai-console/README.MD
mirai-console/README.MD
+0
-88
mirai-console/build.gradle.kts
mirai-console/build.gradle.kts
+0
-46
mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsole.kt
...e/src/main/kotlin/net/mamoe/mirai/console/MiraiConsole.kt
+0
-218
mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/Command.kt
...rc/main/kotlin/net/mamoe/mirai/console/command/Command.kt
+0
-242
mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/DefaultCommands.kt
...kotlin/net/mamoe/mirai/console/command/DefaultCommands.kt
+0
-265
mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/ConfigSection.kt
...n/kotlin/net/mamoe/mirai/console/plugins/ConfigSection.kt
+0
-525
mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginBase.kt
...main/kotlin/net/mamoe/mirai/console/plugins/PluginBase.kt
+0
-410
mirai-console/src/main/kotlin/net/mamoe/mirai/console/pure/MiraiConsolePureLoader.kt
...in/net/mamoe/mirai/console/pure/MiraiConsolePureLoader.kt
+0
-16
mirai-console/src/main/kotlin/net/mamoe/mirai/console/pure/MiraiConsoleUIPure.kt
...kotlin/net/mamoe/mirai/console/pure/MiraiConsoleUIPure.kt
+0
-81
mirai-console/src/main/kotlin/net/mamoe/mirai/console/utils/BotHelper.kt
...rc/main/kotlin/net/mamoe/mirai/console/utils/BotHelper.kt
+0
-41
mirai-console/src/main/kotlin/net/mamoe/mirai/console/utils/MiraiConsoleUI.kt
...in/kotlin/net/mamoe/mirai/console/utils/MiraiConsoleUI.kt
+0
-75
settings.gradle
settings.gradle
+0
-3
No files found.
mirai-console-graphical/README.md
deleted
100644 → 0
View file @
6110cf8f
### Mirai Console Graphical
支持windows/mac
有正式UI界面实现的CONSOLE
优点: 适合新手/完全不懂编程的/界面美丽
缺点: 不能在linux服务器运行
所使用插件系统与terminal版本一致 可以来回切换
\ No newline at end of file
mirai-console-graphical/build.gradle.kts
deleted
100644 → 0
View file @
6110cf8f
plugins
{
id
(
"kotlinx-serialization"
)
id
(
"org.openjfx.javafxplugin"
)
version
"0.0.8"
id
(
"kotlin"
)
id
(
"java"
)
}
javafx
{
version
=
"13.0.2"
modules
=
listOf
(
"javafx.controls"
)
//mainClassName = "Application"
}
apply
(
plugin
=
"com.github.johnrengelman.shadow"
)
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
kotlinx
(
id
:
String
,
version
:
String
)
=
"org.jetbrains.kotlinx:kotlinx-$id:$version"
fun
ktor
(
id
:
String
,
version
:
String
)
=
"io.ktor:ktor-$id:$version"
dependencies
{
api
(
project
(
":mirai-core"
))
api
(
project
(
":mirai-core-qqandroid"
))
api
(
project
(
":mirai-api-http"
))
api
(
project
(
":mirai-console"
))
runtimeOnly
(
files
(
"../mirai-core-qqandroid/build/classes/kotlin/jvm/main"
))
api
(
group
=
"no.tornado"
,
name
=
"tornadofx"
,
version
=
"1.7.19"
)
api
(
group
=
"com.jfoenix"
,
name
=
"jfoenix"
,
version
=
"9.0.8"
)
api
(
"org.bouncycastle:bcprov-jdk15on:1.64"
)
// classpath is not set correctly by IDE
}
tasks
.
withType
<
org
.
jetbrains
.
kotlin
.
gradle
.
tasks
.
KotlinCompile
>
{
kotlinOptions
.
jvmTarget
=
"1.8"
}
\ No newline at end of file
mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/MiraiGraphical.kt
deleted
100644 → 0
View file @
6110cf8f
/*
* 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
*/
package
net.mamoe.mirai.console.graphical
import
net.mamoe.mirai.console.MiraiConsole
import
net.mamoe.mirai.console.graphical.controller.MiraiGraphicalUIController
import
net.mamoe.mirai.console.graphical.styleSheet.PrimaryStyleSheet
import
net.mamoe.mirai.console.graphical.view.Decorator
import
tornadofx.App
import
tornadofx.find
import
tornadofx.launch
fun
main
(
args
:
Array
<
String
>)
{
launch
<
MiraiGraphicalUI
>(
args
)
}
class
MiraiGraphicalUI
:
App
(
Decorator
::
class
,
PrimaryStyleSheet
::
class
)
{
override
fun
init
()
{
super
.
init
()
MiraiConsole
.
start
(
find
<
MiraiGraphicalUIController
>())
}
override
fun
stop
()
{
super
.
stop
()
MiraiConsole
.
stop
()
}
}
mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/controller/MiraiGraphicalUIController.kt
deleted
100644 → 0
View file @
6110cf8f
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
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.utils.MiraiConsoleUI
import
net.mamoe.mirai.utils.LoginSolver
import
tornadofx.*
class
MiraiGraphicalUIController
:
Controller
(),
MiraiConsoleUI
{
private
val
loginSolver
=
GraphicalLoginSolver
()
private
val
cache
=
mutableMapOf
<
Long
,
BotModel
>()
val
mainLog
=
observableListOf
<
String
>()
val
botList
=
observableListOf
<
BotModel
>()
val
pluginList
:
ObservableList
<
PluginModel
>
by
lazy
(
::
getPluginsFromConsole
)
val
consoleInfo
=
ConsoleInfo
()
fun
login
(
qq
:
String
,
psd
:
String
)
{
MiraiConsole
.
CommandProcessor
.
runConsoleCommandBlocking
(
"/login $qq $psd"
)
}
fun
sendCommand
(
command
:
String
)
=
MiraiConsole
.
CommandProcessor
.
runConsoleCommandBlocking
(
command
)
override
fun
pushLog
(
identity
:
Long
,
message
:
String
)
=
Platform
.
runLater
{
when
(
identity
)
{
0L
->
mainLog
.
add
(
message
)
else
->
cache
[
identity
]
?.
logHistory
?.
add
(
message
)
}
}
override
fun
prePushBot
(
identity
:
Long
)
=
Platform
.
runLater
{
BotModel
(
identity
).
also
{
cache
[
identity
]
=
it
botList
.
add
(
it
)
}
}
override
fun
pushBot
(
bot
:
Bot
)
=
Platform
.
runLater
{
cache
[
bot
.
uin
]
?.
bot
=
bot
}
override
fun
pushVersion
(
consoleVersion
:
String
,
consoleBuild
:
String
,
coreVersion
:
String
)
{
Platform
.
runLater
{
consoleInfo
.
consoleVersion
=
consoleVersion
consoleInfo
.
consoleBuild
=
consoleBuild
consoleInfo
.
coreVersion
=
coreVersion
}
}
override
suspend
fun
requestInput
(
question
:
String
):
String
{
val
model
=
VerificationCodeModel
()
find
<
VerificationCodeFragment
>(
Scope
(
model
)).
openModal
(
modality
=
Modality
.
APPLICATION_MODAL
,
resizable
=
false
)
return
model
.
code
.
value
}
override
fun
pushBotAdminStatus
(
identity
:
Long
,
admins
:
List
<
Long
>)
=
Platform
.
runLater
{
cache
[
identity
]
?.
admins
?.
setAll
(
admins
)
}
override
fun
createLoginSolver
():
LoginSolver
=
loginSolver
private
fun
getPluginsFromConsole
():
ObservableList
<
PluginModel
>
=
MiraiConsole
.
pluginManager
.
getAllPluginDescriptions
().
map
(
::
PluginModel
).
toObservable
()
}
class
GraphicalLoginSolver
:
LoginSolver
()
{
override
suspend
fun
onSolvePicCaptcha
(
bot
:
Bot
,
data
:
IoBuffer
):
String
?
{
TODO
(
"not implemented"
)
//To change body of created functions use File | Settings | File Templates.
}
override
suspend
fun
onSolveSliderCaptcha
(
bot
:
Bot
,
url
:
String
):
String
?
{
TODO
(
"not implemented"
)
//To change body of created functions use File | Settings | File Templates.
}
override
suspend
fun
onSolveUnsafeDeviceLoginVerify
(
bot
:
Bot
,
url
:
String
):
String
?
{
TODO
(
"not implemented"
)
//To change body of created functions use File | Settings | File Templates.
}
}
\ No newline at end of file
mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/model/BotModel.kt
deleted
100644 → 0
View file @
6110cf8f
package
net.mamoe.mirai.console.graphical.model
import
javafx.beans.property.SimpleObjectProperty
import
net.mamoe.mirai.Bot
import
tornadofx.*
class
BotModel
(
val
uin
:
Long
)
{
val
botProperty
=
SimpleObjectProperty
<
Bot
>(
null
)
var
bot
:
Bot
by
botProperty
val
logHistory
=
observableListOf
<
String
>()
val
admins
=
observableListOf
<
Long
>()
}
class
BotViewModel
(
botModel
:
BotModel
?
=
null
)
:
ItemViewModel
<
BotModel
>(
botModel
)
{
val
bot
=
bind
(
BotModel
::
botProperty
)
val
logHistory
=
bind
(
BotModel
::
logHistory
)
val
admins
=
bind
(
BotModel
::
admins
)
}
\ No newline at end of file
mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/model/ConsoleInfo.kt
deleted
100644 → 0
View file @
6110cf8f
package
net.mamoe.mirai.console.graphical.model
import
javafx.beans.property.SimpleStringProperty
import
tornadofx.setValue
import
tornadofx.getValue
class
ConsoleInfo
{
val
consoleVersionProperty
=
SimpleStringProperty
()
var
consoleVersion
by
consoleVersionProperty
val
consoleBuildProperty
=
SimpleStringProperty
()
var
consoleBuild
by
consoleBuildProperty
val
coreVersionProperty
=
SimpleStringProperty
()
var
coreVersion
by
coreVersionProperty
}
\ No newline at end of file
mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/model/PluginModel.kt
deleted
100644 → 0
View file @
6110cf8f
package
net.mamoe.mirai.console.graphical.model
import
com.jfoenix.controls.datamodels.treetable.RecursiveTreeObject
import
javafx.beans.property.SimpleBooleanProperty
import
javafx.beans.property.SimpleStringProperty
import
net.mamoe.mirai.console.plugins.PluginDescription
import
tornadofx.getValue
import
tornadofx.setValue
class
PluginModel
(
val
name
:
String
,
val
version
:
String
,
val
author
:
String
,
val
description
:
String
)
:
RecursiveTreeObject
<
PluginModel
>()
{
constructor
(
plugin
:
PluginDescription
):
this
(
plugin
.
name
,
plugin
.
version
,
plugin
.
author
,
plugin
.
info
)
val
enabledProperty
=
SimpleBooleanProperty
(
this
,
"enabledProperty"
)
var
enabled
by
enabledProperty
}
\ No newline at end of file
mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/model/VerificationCodeModel.kt
deleted
100644 → 0
View file @
6110cf8f
package
net.mamoe.mirai.console.graphical.model
import
javafx.beans.property.SimpleStringProperty
import
tornadofx.ItemViewModel
import
tornadofx.getValue
import
tornadofx.setValue
class
VerificationCode
{
val
codeProperty
=
SimpleStringProperty
(
""
)
var
code
:
String
by
codeProperty
}
class
VerificationCodeModel
(
code
:
VerificationCode
)
:
ItemViewModel
<
VerificationCode
>(
code
)
{
constructor
():
this
(
VerificationCode
())
val
code
=
bind
(
VerificationCode
::
codeProperty
)
}
\ No newline at end of file
mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/styleSheet/LoginViewStyleSheet.kt
deleted
100644 → 0
View file @
6110cf8f
package
net.mamoe.mirai.console.graphical.styleSheet
import
javafx.scene.Cursor
import
javafx.scene.effect.BlurType
import
javafx.scene.effect.DropShadow
import
javafx.scene.paint.Color
import
javafx.scene.text.FontWeight
import
tornadofx.*
class
LoginViewStyleSheet
:
Stylesheet
()
{
companion
object
{
val
vBox
by
csselement
(
"VBox"
)
}
init
{
vBox
{
maxWidth
=
500
.
px
maxHeight
=
500
.
px
backgroundColor
+=
c
(
"39c5BB"
,
0.3
)
backgroundRadius
+=
box
(
15
.
px
)
padding
=
box
(
50
.
px
,
100
.
px
)
spacing
=
25
.
px
borderRadius
+=
box
(
15
.
px
)
effect
=
DropShadow
(
BlurType
.
THREE_PASS_BOX
,
Color
.
GRAY
,
10.0
,
0.0
,
15.0
,
15.0
)
}
textField
{
prefHeight
=
30
.
px
textFill
=
Color
.
BLACK
fontWeight
=
FontWeight
.
BOLD
}
button
{
backgroundColor
+=
c
(
"00BCD4"
,
0.8
)
padding
=
box
(
10
.
px
,
0
.
px
)
prefWidth
=
500
.
px
textFill
=
Color
.
WHITE
fontWeight
=
FontWeight
.
BOLD
cursor
=
Cursor
.
HAND
}
}
}
\ No newline at end of file
mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/styleSheet/PrimaryStyleSheet.kt
deleted
100644 → 0
View file @
6110cf8f
package
net.mamoe.mirai.console.graphical.styleSheet
import
tornadofx.*
class
PrimaryStyleSheet
:
Stylesheet
()
{
companion
object
{
val
jfxTitle
by
cssclass
(
"jfx-decorator-buttons-container"
)
val
container
by
cssclass
(
"jfx-decorator-content-container"
)
}
init
{
jfxTitle
{
backgroundColor
+=
c
(
"00BCD4"
)
}
container
{
borderColor
+=
box
(
c
(
"00BCD4"
))
borderWidth
+=
box
(
0
.
px
,
4
.
px
,
4
.
px
,
4
.
px
)
}
}
}
\ No newline at end of file
mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/util/JFoenixAdaptor.kt
deleted
100644 → 0
View file @
6110cf8f
package
net.mamoe.mirai.console.graphical.util
import
com.jfoenix.controls.*
import
com.jfoenix.controls.datamodels.treetable.RecursiveTreeObject
import
javafx.beans.value.ObservableValue
import
javafx.collections.ObservableList
import
javafx.event.EventTarget
import
javafx.scene.Node
import
javafx.scene.control.*
import
tornadofx.SortedFilteredList
import
tornadofx.attachTo
import
tornadofx.bind
internal
fun
EventTarget
.
jfxTabPane
(
op
:
TabPane
.()
->
Unit
=
{})
=
JFXTabPane
().
attachTo
(
this
,
op
)
internal
fun
EventTarget
.
jfxButton
(
text
:
String
=
""
,
graphic
:
Node
?
=
null
,
op
:
Button
.()
->
Unit
=
{})
=
JFXButton
(
text
).
attachTo
(
this
,
op
)
{
if
(
graphic
!=
null
)
it
.
graphic
=
graphic
}
fun
EventTarget
.
jfxTextfield
(
value
:
String
?
=
null
,
op
:
JFXTextField
.()
->
Unit
=
{})
=
JFXTextField
().
attachTo
(
this
,
op
)
{
if
(
value
!=
null
)
it
.
text
=
value
}
fun
EventTarget
.
jfxTextfield
(
property
:
ObservableValue
<
String
>,
op
:
JFXTextField
.()
->
Unit
=
{})
=
jfxTextfield
().
apply
{
bind
(
property
)
op
(
this
)
}
fun
EventTarget
.
jfxPasswordfield
(
value
:
String
?
=
null
,
op
:
JFXPasswordField
.()
->
Unit
=
{})
=
JFXPasswordField
().
attachTo
(
this
,
op
)
{
if
(
value
!=
null
)
it
.
text
=
value
}
fun
EventTarget
.
jfxPasswordfield
(
property
:
ObservableValue
<
String
>,
op
:
JFXPasswordField
.()
->
Unit
=
{})
=
jfxPasswordfield
().
apply
{
bind
(
property
)
op
(
this
)
}
internal
fun
<
T
>
EventTarget
.
jfxListView
(
values
:
ObservableList
<
T
>?
=
null
,
op
:
ListView
<
T
>.()
->
Unit
=
{})
=
JFXListView
<
T
>().
attachTo
(
this
,
op
)
{
if
(
values
!=
null
)
{
if
(
values
is
SortedFilteredList
<
T
>)
values
.
bindTo
(
it
)
else
it
.
items
=
values
}
}
fun
<
T
:
RecursiveTreeObject
<
T
>
?
>
EventTarget
.
jfxTreeTableView
(
items
:
ObservableList
<
T
>?
=
null
,
op
:
JFXTreeTableView
<
T
>.()
->
Unit
=
{})
=
JFXTreeTableView
<
T
>(
RecursiveTreeItem
(
items
,
RecursiveTreeObject
<
T
>
::
getChildren
)).
attachTo
(
this
,
op
)
\ No newline at end of file
mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/view/Decorator.kt
deleted
100644 → 0
View file @
6110cf8f
package
net.mamoe.mirai.console.graphical.view
import
com.jfoenix.controls.JFXDecorator
import
tornadofx.View
class
Decorator
:
View
()
{
override
val
root
=
JFXDecorator
(
primaryStage
,
find
<
PrimaryView
>().
root
)
}
\ No newline at end of file
mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/view/LoginView.kt
deleted
100644 → 0
View file @
6110cf8f
package
net.mamoe.mirai.console.graphical.view
import
javafx.beans.property.SimpleStringProperty
import
javafx.geometry.Pos
import
javafx.scene.image.Image
import
kotlinx.coroutines.runBlocking
import
net.mamoe.mirai.console.graphical.controller.MiraiGraphicalUIController
import
net.mamoe.mirai.console.graphical.styleSheet.LoginViewStyleSheet
import
net.mamoe.mirai.console.graphical.util.jfxButton
import
net.mamoe.mirai.console.graphical.util.jfxPasswordfield
import
net.mamoe.mirai.console.graphical.util.jfxTextfield
import
tornadofx.*
class
LoginView
:
View
(
"CNM"
)
{
private
val
controller
=
find
<
MiraiGraphicalUIController
>()
private
val
qq
=
SimpleStringProperty
(
""
)
private
val
psd
=
SimpleStringProperty
(
""
)
override
val
root
=
borderpane
{
addStylesheet
(
LoginViewStyleSheet
::
class
)
center
=
vbox
{
imageview
(
Image
(
LoginView
::
class
.
java
.
classLoader
.
getResourceAsStream
(
"character.png"
)))
{
alignment
=
Pos
.
CENTER
}
jfxTextfield
(
qq
)
{
promptText
=
"QQ"
isLabelFloat
=
true
}
jfxPasswordfield
(
psd
)
{
promptText
=
"Password"
isLabelFloat
=
true
}
jfxButton
(
"Login"
).
action
{
runAsync
{
runBlocking
{
controller
.
login
(
qq
.
value
,
psd
.
value
)
}
}.
ui
{
qq
.
value
=
""
psd
.
value
=
""
}
}
}
}
}
\ No newline at end of file
mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/view/PluginsView.kt
deleted
100644 → 0
View file @
6110cf8f
package
net.mamoe.mirai.console.graphical.view
import
com.jfoenix.controls.JFXTreeTableColumn
import
net.mamoe.mirai.console.graphical.controller.MiraiGraphicalUIController
import
net.mamoe.mirai.console.graphical.model.PluginModel
import
net.mamoe.mirai.console.graphical.util.jfxTreeTableView
import
tornadofx.View
class
PluginsView
:
View
()
{
private
val
controller
=
find
<
MiraiGraphicalUIController
>()
val
plugins
=
controller
.
pluginList
override
val
root
=
jfxTreeTableView
(
plugins
)
{
columns
.
addAll
(
JFXTreeTableColumn
<
PluginModel
,
String
>(
"插件名"
).
apply
{
prefWidthProperty
().
bind
(
this
@
jfxTreeTableView
.
widthProperty
().
multiply
(
0.1
))
},
JFXTreeTableColumn
<
PluginModel
,
String
>(
"版本"
).
apply
{
prefWidthProperty
().
bind
(
this
@
jfxTreeTableView
.
widthProperty
().
multiply
(
0.1
))
},
JFXTreeTableColumn
<
PluginModel
,
String
>(
"作者"
).
apply
{
prefWidthProperty
().
bind
(
this
@
jfxTreeTableView
.
widthProperty
().
multiply
(
0.1
))
},
JFXTreeTableColumn
<
PluginModel
,
String
>(
"介绍"
).
apply
{
prefWidthProperty
().
bind
(
this
@
jfxTreeTableView
.
widthProperty
().
multiply
(
0.6
))
},
JFXTreeTableColumn
<
PluginModel
,
String
>(
"操作"
).
apply
{
prefWidthProperty
().
bind
(
this
@
jfxTreeTableView
.
widthProperty
().
multiply
(
0.08
))
}
)
}
}
\ No newline at end of file
mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/view/PrimaryView.kt
deleted
100644 → 0
View file @
6110cf8f
package
net.mamoe.mirai.console.graphical.view
import
com.jfoenix.controls.*
import
javafx.collections.ObservableList
import
javafx.scene.control.Tab
import
javafx.scene.control.TabPane
import
javafx.scene.image.Image
import
javafx.scene.input.KeyCode
import
kotlinx.coroutines.runBlocking
import
net.mamoe.mirai.console.graphical.controller.MiraiGraphicalUIController
import
net.mamoe.mirai.console.graphical.model.BotModel
import
net.mamoe.mirai.console.graphical.util.jfxListView
import
net.mamoe.mirai.console.graphical.util.jfxTabPane
import
tornadofx.*
class
PrimaryView
:
View
()
{
private
val
controller
=
find
<
MiraiGraphicalUIController
>()
override
val
root
=
borderpane
{
prefWidth
=
1000.0
prefHeight
=
650.0
left
=
vbox
{
imageview
(
Image
(
PrimaryView
::
class
.
java
.
classLoader
.
getResourceAsStream
(
"logo.png"
)))
// bot list
jfxListView
(
controller
.
botList
)
{
fitToParentSize
()
setCellFactory
{
object
:
JFXListCell
<
BotModel
>()
{
init
{
onDoubleClick
{
(
center
as
TabPane
).
logTab
(
text
=
item
.
uin
.
toString
(),
logs
=
item
.
logHistory
).
select
()
}
}
override
fun
updateItem
(
item
:
BotModel
?,
empty
:
Boolean
)
{
super
.
updateItem
(
item
,
empty
)
if
(
item
!=
null
&&
!
empty
)
{
graphic
=
null
text
=
item
.
uin
.
toString
()
}
else
{
graphic
=
null
text
=
""
}
}
}
}
}
// command input
textfield
{
setOnKeyPressed
{
if
(
it
.
code
==
KeyCode
.
ENTER
)
{
runAsync
{
runBlocking
{
controller
.
sendCommand
(
text
)
}
}.
ui
{
text
=
""
}
}
}
}
}
center
=
jfxTabPane
{
tab
(
"Login"
).
content
=
find
<
LoginView
>().
root
tab
(
"Plugins"
).
content
=
find
<
PluginsView
>().
root
tab
(
"Settings"
).
content
=
find
<
SettingsView
>().
root
logTab
(
"Main"
,
controller
.
mainLog
)
}
}
}
private
fun
TabPane
.
logTab
(
text
:
String
?
=
null
,
logs
:
ObservableList
<
String
>,
op
:
Tab
.()
->
Unit
=
{}
)=
tab
(
text
)
{
listview
(
logs
)
{
fitToParentSize
()
cellFormat
{
graphic
=
label
(
it
)
{
maxWidthProperty
().
bind
(
this
@
listview
.
widthProperty
())
isWrapText
=
true
}
}
}
also
(
op
)
}
\ No newline at end of file
mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/view/SettingsView.kt
deleted
100644 → 0
View file @
6110cf8f
package
net.mamoe.mirai.console.graphical.view
import
net.mamoe.mirai.console.graphical.controller.MiraiGraphicalUIController
import
net.mamoe.mirai.console.graphical.util.jfxButton
import
net.mamoe.mirai.console.graphical.util.jfxTextfield
import
tornadofx.*
class
SettingsView
:
View
()
{
private
val
controller
=
find
<
MiraiGraphicalUIController
>()
override
val
root
=
form
{
fieldset
{
field
{
jfxButton
(
"撤掉"
)
{
}
jfxButton
(
"保存"
)
{
}
}
}
fieldset
(
"插件目录"
)
{
field
{
jfxTextfield
(
"..."
)
{
isEditable
=
false
}
jfxButton
(
"打开目录"
)
}
}
fieldset
(
"最大日志容量"
)
{
field
{
jfxTextfield
(
"..."
)
{
}
}
}
}
}
\ No newline at end of file
mirai-console-graphical/src/main/kotlin/net/mamoe/mirai/console/graphical/view/VerificationCodeFragment.kt
deleted
100644 → 0
View file @
6110cf8f
package
net.mamoe.mirai.console.graphical.view
import
javafx.scene.Parent
import
tornadofx.*
class
VerificationCodeFragment
:
Fragment
()
{
override
val
root
=
vbox
{
//TODO: 显示验证码
form
{
fieldset
{
field
(
"验证码"
)
{
textfield
()
}
}
}
}
}
\ No newline at end of file
mirai-console-graphical/src/main/resources/character.png
deleted
100644 → 0
View file @
6110cf8f
29.7 KB
mirai-console-graphical/src/main/resources/logo.png
deleted
100644 → 0
View file @
6110cf8f
4.64 KB
mirai-console-terminal/README.md
deleted
100644 → 0
View file @
6110cf8f
### Mirai Console Terminal
支持windows/mac/linux
在terminal环境下的Console, 由控制台富文本实现简易UI
优点: 可以在linux环境下运行/简洁使用效率高
缺点: 需要有略微的terminal知识
所使用插件系统与graphical版本一致 可以来回切换
\ No newline at end of file
mirai-console-terminal/build.gradle.kts
deleted
100644 → 0
View file @
6110cf8f
plugins
{
id
(
"kotlinx-serialization"
)
id
(
"kotlin"
)
id
(
"java"
)
}
apply
(
plugin
=
"com.github.johnrengelman.shadow"
)
tasks
.
withType
<
com
.
github
.
jengelman
.
gradle
.
plugins
.
shadow
.
tasks
.
ShadowJar
>()
{
manifest
{
attributes
[
"Main-Class"
]
=
"net.mamoe.mirai.console.MiraiConsoleTerminalLoader"
}
}
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
kotlinx
(
id
:
String
,
version
:
String
)
=
"org.jetbrains.kotlinx:kotlinx-$id:$version"
fun
ktor
(
id
:
String
,
version
:
String
)
=
"io.ktor:ktor-$id:$version"
dependencies
{
api
(
project
(
":mirai-core"
))
api
(
project
(
":mirai-core-qqandroid"
))
api
(
project
(
":mirai-api-http"
))
api
(
project
(
":mirai-console"
))
runtimeOnly
(
files
(
"../mirai-core-qqandroid/build/classes/kotlin/jvm/main"
))
runtimeOnly
(
files
(
"../mirai-core/build/classes/kotlin/jvm/main"
))
api
(
group
=
"com.googlecode.lanterna"
,
name
=
"lanterna"
,
version
=
"3.0.2"
)
api
(
"org.bouncycastle:bcprov-jdk15on:1.64"
)
// classpath is not set correctly by IDE
}
\ No newline at end of file
mirai-console-terminal/src/main/kotlin/net/mamoe/mirai/console/MiraiConsoleTerminalLoader.kt
deleted
100644 → 0
View file @
6110cf8f
package
net.mamoe.mirai.console
import
net.mamoe.mirai.console.pure.MiraiConsoleUIPure
import
kotlin.concurrent.thread
class
MiraiConsoleTerminalLoader
{
companion
object
{
@JvmStatic
fun
main
(
args
:
Array
<
String
>)
{
if
(
args
.
contains
(
"pure"
)
||
args
.
contains
(
"-pure"
)
||
System
.
getProperty
(
"os.name"
,
""
).
toLowerCase
().
contains
(
"windows"
)
)
{
println
(
"[MiraiConsoleTerminalLoader]: 将以Pure[兼容模式]启动Console"
)
MiraiConsole
.
start
(
MiraiConsoleUIPure
())
}
else
{
MiraiConsoleTerminalUI
.
start
()
thread
{
MiraiConsole
.
start
(
MiraiConsoleTerminalUI
)
}
}
Runtime
.
getRuntime
().
addShutdownHook
(
thread
(
start
=
false
)
{
MiraiConsole
.
stop
()
MiraiConsoleTerminalUI
.
exit
()
})
}
}
}
\ No newline at end of file
mirai-console-terminal/src/main/kotlin/net/mamoe/mirai/console/MiraiConsoleTerminalUI.kt
deleted
100644 → 0
View file @
6110cf8f
package
net.mamoe.mirai.console
import
com.googlecode.lanterna.SGR
import
com.googlecode.lanterna.TerminalSize
import
com.googlecode.lanterna.TextColor
import
com.googlecode.lanterna.graphics.TextGraphics
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.*
import
kotlinx.coroutines.io.close
import
kotlinx.io.core.IoBuffer
import
kotlinx.io.core.use
import
net.mamoe.mirai.Bot
import
net.mamoe.mirai.console.MiraiConsoleTerminalUI.LoggerDrawer.cleanPage
import
net.mamoe.mirai.console.MiraiConsoleTerminalUI.LoggerDrawer.drawLog
import
net.mamoe.mirai.console.MiraiConsoleTerminalUI.LoggerDrawer.redrawLogs
import
net.mamoe.mirai.console.utils.MiraiConsoleUI
import
net.mamoe.mirai.utils.LoginSolver
import
net.mamoe.mirai.utils.createCharImg
import
net.mamoe.mirai.utils.writeChannel
import
java.io.File
import
java.io.OutputStream
import
java.io.PrintStream
import
java.nio.charset.Charset
import
java.util.*
import
java.util.concurrent.ConcurrentHashMap
import
java.util.concurrent.ConcurrentLinkedDeque
import
javax.imageio.ImageIO
import
kotlin.concurrent.thread
import
kotlin.system.exitProcess
/**
* 此文件不推荐任何人看
* 可能导致
* 1:心肌梗死
* 2:呼吸困难
* 3:想要重写但是发现改任何一个看似不合理的地方都会崩
*
* @author NaturalHG
*
*/
fun
String
.
actualLength
():
Int
{
var
x
=
0
this
.
forEach
{
if
(
it
.
isChineseChar
())
{
x
+=
2
}
else
{
x
+=
1
}
}
return
x
}
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
}
fun
Char
.
isChineseChar
():
Boolean
{
return
this
.
toString
().
isChineseChar
()
}
fun
String
.
isChineseChar
():
Boolean
{
return
this
.
matches
(
Regex
(
"[\u4e00-\u9fa5]"
))
}
object
MiraiConsoleTerminalUI
:
MiraiConsoleUI
{
val
cacheLogSize
=
50
var
mainTitle
=
"Mirai Console v0.01 Core v0.15"
override
fun
pushVersion
(
consoleVersion
:
String
,
consoleBuild
:
String
,
coreVersion
:
String
)
{
mainTitle
=
"Mirai Console(Terminal) $consoleVersion $consoleBuild Core $coreVersion"
}
override
fun
pushLog
(
identity
:
Long
,
message
:
String
)
{
log
[
identity
]
!!
.
push
(
message
)
if
(
identity
==
screens
[
currentScreenId
])
{
drawLog
(
message
)
}
}
override
fun
prePushBot
(
identity
:
Long
)
{
log
[
identity
]
=
LimitLinkedQueue
(
cacheLogSize
)
}
override
fun
pushBot
(
bot
:
Bot
)
{
botAdminCount
[
bot
.
uin
]
=
0
screens
.
add
(
bot
.
uin
)
drawFrame
(
this
.
getScreenName
(
currentScreenId
))
if
(
terminal
is
SwingTerminalFrame
)
{
terminal
.
flush
()
}
}
var
requesting
=
false
var
requestResult
:
String
?
=
null
override
suspend
fun
requestInput
(
question
:
String
):
String
{
requesting
=
true
while
(
requesting
)
{
delay
(
100
)
//不然会卡死 迷惑吧
}
return
requestResult
!!
}
suspend
fun
provideInput
(
input
:
String
)
{
if
(
requesting
)
{
requestResult
=
input
requesting
=
false
}
else
{
MiraiConsole
.
CommandProcessor
.
runConsoleCommand
(
commandBuilder
.
toString
())
}
}
override
fun
pushBotAdminStatus
(
identity
:
Long
,
admins
:
List
<
Long
>)
{
botAdminCount
[
identity
]
=
admins
.
size
}
override
fun
createLoginSolver
():
LoginSolver
{
return
object
:
LoginSolver
()
{
override
suspend
fun
onSolvePicCaptcha
(
bot
:
Bot
,
data
:
IoBuffer
):
String
?
{
val
tempFile
:
File
=
createTempFile
(
suffix
=
".png"
).
apply
{
deleteOnExit
()
}
withContext
(
Dispatchers
.
IO
)
{
tempFile
.
createNewFile
()
pushLog
(
0
,
"[Login Solver]需要图片验证码登录, 验证码为 4 字母"
)
try
{
tempFile
.
writeChannel
().
apply
{
writeFully
(
data
)
close
()
}
pushLog
(
0
,
"请查看文件 ${tempFile.absolutePath}"
)
}
catch
(
e
:
Exception
)
{
error
(
"[Login Solver]验证码无法保存[Error0001]"
)
}
}
var
toLog
=
""
tempFile
.
inputStream
().
use
{
val
img
=
ImageIO
.
read
(
it
)
if
(
img
==
null
)
{
toLog
+=
"无法创建字符图片. 请查看文件\n"
}
else
{
toLog
+=
img
.
createCharImg
((
terminal
.
terminalSize
.
columns
/
1.5
).
toInt
())
}
}
pushLog
(
0
,
"$toLog[Login Solver]请输验证码. ${tempFile.absolutePath}"
)
return
requestInput
(
"[Login Solver]请输入 4 位字母验证码. 若要更换验证码, 请直接回车"
)
!!
.
takeUnless
{
it
.
isEmpty
()
||
it
.
length
!=
4
}
.
also
{
pushLog
(
0
,
"[Login Solver]正在提交[$it]中..."
)
}
}
override
suspend
fun
onSolveSliderCaptcha
(
bot
:
Bot
,
url
:
String
):
String
?
{
pushLog
(
0
,
"[Login Solver]需要滑动验证码"
)
pushLog
(
0
,
"[Login Solver]请在任意浏览器中打开以下链接并完成验证码. "
)
pushLog
(
0
,
"[Login Solver]完成后请输入任意字符 "
)
pushLog
(
0
,
url
)
return
requestInput
(
"[Login Solver]完成后请输入任意字符"
).
also
{
pushLog
(
0
,
"[Login Solver]正在提交中"
)
}
}
override
suspend
fun
onSolveUnsafeDeviceLoginVerify
(
bot
:
Bot
,
url
:
String
):
String
?
{
pushLog
(
0
,
"[Login Solver]需要进行账户安全认证"
)
pushLog
(
0
,
"[Login Solver]该账户有[设备锁]/[不常用登录地点]/[不常用设备登录]的问题"
)
pushLog
(
0
,
"[Login Solver]完成以下账号认证即可成功登录|理论本认证在mirai每个账户中最多出现1次"
)
pushLog
(
0
,
"[Login Solver]请将该链接在QQ浏览器中打开并完成认证, 成功后输入任意字符"
)
pushLog
(
0
,
"[Login Solver]这步操作将在后续的版本中优化"
)
pushLog
(
0
,
url
)
return
requestInput
(
"[Login Solver]完成后请输入任意字符"
).
also
{
pushLog
(
0
,
"[Login Solver]正在提交中..."
)
}
}
}
}
val
log
=
ConcurrentHashMap
<
Long
,
LimitLinkedQueue
<
String
>>().
also
{
it
[
0L
]
=
LimitLinkedQueue
(
cacheLogSize
)
}
val
botAdminCount
=
ConcurrentHashMap
<
Long
,
Int
>()
private
val
screens
=
mutableListOf
(
0L
)
private
var
currentScreenId
=
0
lateinit
var
terminal
:
Terminal
lateinit
var
textGraphics
:
TextGraphics
var
hasStart
=
false
private
lateinit
var
internalPrinter
:
PrintStream
fun
start
()
{
if
(
hasStart
)
{
return
}
internalPrinter
=
System
.
out
hasStart
=
true
val
defaultTerminalFactory
=
DefaultTerminalFactory
(
internalPrinter
,
System
.
`in`
,
Charset
.
defaultCharset
())
try
{
terminal
=
defaultTerminalFactory
.
createTerminal
()
terminal
.
enterPrivateMode
()
terminal
.
clearScreen
()
terminal
.
setCursorVisible
(
false
)
}
catch
(
e
:
Exception
)
{
try
{
terminal
=
SwingTerminalFrame
(
"Mirai Console"
)
terminal
.
enterPrivateMode
()
terminal
.
clearScreen
()
terminal
.
setCursorVisible
(
false
)
}
catch
(
e
:
Exception
)
{
error
(
"can not create terminal"
)
}
}
textGraphics
=
terminal
.
newTextGraphics
()
/*
var lastRedrawTime = 0L
var lastNewWidth = 0
var lastNewHeight = 0
terminal.addResizeListener(TerminalResizeListener { terminal1: Terminal, newSize: TerminalSize ->
try {
if (lastNewHeight == newSize.rows
&&
lastNewWidth == newSize.columns
) {
return@TerminalResizeListener
}
lastNewHeight = newSize.rows
lastNewWidth = newSize.columns
terminal.clearScreen()
if(terminal !is SwingTerminalFrame) {
Thread.sleep(300)
}
update()
redrawCommand()
redrawLogs(log[screens[currentScreenId]]!!)
}catch (ignored:Exception){
}
})
*/
var
lastJob
:
Job
?
=
null
terminal
.
addResizeListener
(
TerminalResizeListener
{
terminal1
:
Terminal
,
newSize
:
TerminalSize
->
lastJob
=
GlobalScope
.
launch
{
try
{
delay
(
300
)
if
(
lastJob
==
coroutineContext
[
Job
])
{
terminal
.
clearScreen
()
//inited = false
update
()
redrawCommand
()
redrawLogs
(
log
[
screens
[
currentScreenId
]]
!!
)
}
}
catch
(
e
:
Exception
)
{
pushLog
(
0
,
"[UI ERROR] ${e.message}"
)
}
}
})
if
(
terminal
!
is
SwingTerminalFrame
)
{
System
.
setOut
(
PrintStream
(
object
:
OutputStream
()
{
var
builder
=
java
.
lang
.
StringBuilder
()
override
fun
write
(
b
:
Int
)
{
with
(
b
.
toChar
())
{
if
(
this
==
'\n'
)
{
pushLog
(
0
,
builder
.
toString
())
builder
=
java
.
lang
.
StringBuilder
()
}
else
{
builder
.
append
(
this
)
}
}
}
}))
}
System
.
setErr
(
System
.
out
)
try
{
update
()
}
catch
(
e
:
Exception
)
{
pushLog
(
0
,
"[UI ERROR] ${e.message}"
)
}
val
charList
=
listOf
(
','
,
'.'
,
'/'
,
'@'
,
'#'
,
'$'
,
'%'
,
'^'
,
'&'
,
'*'
,
'('
,
')'
,
'_'
,
'='
,
'+'
,
'!'
,
' '
)
thread
{
while
(
true
)
{
try
{
var
keyStroke
:
KeyStroke
=
terminal
.
readInput
()
when
(
keyStroke
.
keyType
)
{
KeyType
.
ArrowLeft
->
{
currentScreenId
=
getLeftScreenId
()
clearRows
(
2
)
cleanPage
()
update
()
}
KeyType
.
ArrowRight
->
{
currentScreenId
=
getRightScreenId
()
clearRows
(
2
)
cleanPage
()
update
()
}
KeyType
.
Enter
->
{
runBlocking
{
provideInput
(
commandBuilder
.
toString
())
}
emptyCommand
()
}
KeyType
.
Escape
->
{
exit
()
}
else
->
{
if
(
keyStroke
.
character
!=
null
)
{
if
(
keyStroke
.
character
.
toInt
()
==
8
)
{
deleteCommandChar
()
}
if
(
keyStroke
.
character
.
isLetterOrDigit
()
||
charList
.
contains
(
keyStroke
.
character
))
{
addCommandChar
(
keyStroke
.
character
)
}
}
}
}
}
catch
(
e
:
Exception
)
{
pushLog
(
0
,
"[UI ERROR] ${e.message}"
)
}
}
}
}
private
fun
getLeftScreenId
():
Int
{
var
newId
=
currentScreenId
-
1
if
(
newId
<
0
)
{
newId
=
screens
.
size
-
1
}
return
newId
}
private
fun
getRightScreenId
():
Int
{
var
newId
=
1
+
currentScreenId
if
(
newId
>=
screens
.
size
)
{
newId
=
0
}
return
newId
}
private
fun
getScreenName
(
id
:
Int
):
String
{
return
when
(
screens
[
id
])
{
0L
->
{
"Console Screen"
}
else
->
{
"Bot: ${screens[id]}"
}
}
}
fun
clearRows
(
row
:
Int
)
{
textGraphics
.
putString
(
0
,
row
,
" "
.
repeat
(
terminal
.
terminalSize
.
columns
)
)
}
fun
drawFrame
(
title
:
String
)
{
val
width
=
terminal
.
terminalSize
.
columns
val
height
=
terminal
.
terminalSize
.
rows
terminal
.
setBackgroundColor
(
TextColor
.
ANSI
.
DEFAULT
)
textGraphics
.
foregroundColor
=
TextColor
.
ANSI
.
WHITE
textGraphics
.
backgroundColor
=
TextColor
.
ANSI
.
GREEN
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
))
textGraphics
.
putString
(
2
,
5
,
"-"
.
repeat
(
width
-
4
))
textGraphics
.
putString
(
2
,
height
-
4
,
"-"
.
repeat
(
width
-
4
))
textGraphics
.
putString
(
2
,
height
-
3
,
"|>>>"
)
textGraphics
.
putString
(
width
-
3
,
height
-
3
,
"|"
)
textGraphics
.
putString
(
2
,
height
-
2
,
"-"
.
repeat
(
width
-
4
))
textGraphics
.
foregroundColor
=
TextColor
.
ANSI
.
DEFAULT
textGraphics
.
backgroundColor
=
TextColor
.
ANSI
.
DEFAULT
val
leftName
=
getScreenName
(
getLeftScreenId
())
// clearRows(2)
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
.
foregroundColor
=
TextColor
.
ANSI
.
DEFAULT
textGraphics
.
backgroundColor
=
TextColor
.
ANSI
.
DEFAULT
val
rightName
=
getScreenName
(
getRightScreenId
())
textGraphics
.
putString
((
width
+
title
.
actualLength
())
/
2
+
1
,
2
,
">> $rightName"
)
}
fun
drawMainFrame
(
onlineBotCount
:
Number
)
{
drawFrame
(
"Console Screen"
)
val
width
=
terminal
.
terminalSize
.
columns
textGraphics
.
foregroundColor
=
TextColor
.
ANSI
.
DEFAULT
textGraphics
.
backgroundColor
=
TextColor
.
ANSI
.
DEFAULT
clearRows
(
4
)
textGraphics
.
putString
(
2
,
4
,
"|Online Bots: $onlineBotCount"
)
textGraphics
.
putString
(
width
-
2
-
"Powered By Mamoe Technologies|"
.
actualLength
(),
4
,
"Powered By Mamoe Technologies|"
)
}
fun
drawBotFrame
(
qq
:
Long
,
adminCount
:
Number
)
{
drawFrame
(
"Bot: $qq"
)
val
width
=
terminal
.
terminalSize
.
columns
textGraphics
.
foregroundColor
=
TextColor
.
ANSI
.
DEFAULT
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|"
)
}
object
LoggerDrawer
{
var
currentHeight
=
6
fun
drawLog
(
string
:
String
,
flush
:
Boolean
=
true
)
{
val
maxHeight
=
terminal
.
terminalSize
.
rows
-
4
val
heightNeed
=
(
string
.
actualLength
()
/
(
terminal
.
terminalSize
.
columns
-
6
))
+
1
if
(
heightNeed
-
1
>
maxHeight
)
{
pushLog
(
0
,
"[UI ERROR]: 您的屏幕太小, 有一条超长LOG无法显示"
)
return
//拒绝打印
}
if
(
currentHeight
+
heightNeed
>
maxHeight
)
{
cleanPage
()
//翻页
}
if
(
string
.
contains
(
"\n"
))
{
string
.
split
(
"\n"
).
forEach
{
drawLog
(
string
,
false
)
}
}
else
{
val
width
=
terminal
.
terminalSize
.
columns
-
6
var
x
=
string
while
(
true
)
{
if
(
x
==
""
)
{
break
}
val
toWrite
=
if
(
x
.
actualLength
()
>
width
)
{
val
index
=
x
.
getSubStringIndexByActualLength
(
width
)
x
.
substring
(
0
,
index
).
also
{
x
=
if
(
index
<
x
.
length
)
{
x
.
substring
(
index
)
}
else
{
""
}
}
}
else
{
x
.
also
{
x
=
""
}
}
try
{
textGraphics
.
foregroundColor
=
TextColor
.
ANSI
.
GREEN
textGraphics
.
backgroundColor
=
TextColor
.
ANSI
.
DEFAULT
textGraphics
.
putString
(
3
,
currentHeight
,
toWrite
,
SGR
.
ITALIC
)
}
catch
(
ignored
:
Exception
)
{
//
}
++
currentHeight
}
}
if
(
flush
&&
terminal
is
SwingTerminalFrame
)
{
terminal
.
flush
()
}
}
fun
cleanPage
()
{
for
(
index
in
6
until
terminal
.
terminalSize
.
rows
-
4
)
{
clearRows
(
index
)
}
currentHeight
=
6
}
fun
redrawLogs
(
toDraw
:
Queue
<
String
>)
{
//this.cleanPage()
currentHeight
=
6
var
logsToDraw
=
0
var
vara
=
0
val
toPrint
=
mutableListOf
<
String
>()
toDraw
.
forEach
{
val
heightNeed
=
(
it
.
actualLength
()
/
(
terminal
.
terminalSize
.
columns
-
6
))
+
1
vara
+=
heightNeed
if
(
currentHeight
+
vara
<
terminal
.
terminalSize
.
rows
-
4
)
{
logsToDraw
++
toPrint
.
add
(
it
)
}
else
{
return
@
forEach
}
}
toPrint
.
reversed
().
forEach
{
drawLog
(
it
,
false
)
}
if
(
terminal
is
SwingTerminalFrame
)
{
terminal
.
flush
()
}
}
}
var
commandBuilder
=
StringBuilder
()
fun
redrawCommand
()
{
val
height
=
terminal
.
terminalSize
.
rows
val
width
=
terminal
.
terminalSize
.
columns
clearRows
(
height
-
3
)
textGraphics
.
foregroundColor
=
TextColor
.
ANSI
.
DEFAULT
textGraphics
.
putString
(
2
,
height
-
3
,
"|>>>"
)
textGraphics
.
putString
(
width
-
3
,
height
-
3
,
"|"
)
textGraphics
.
foregroundColor
=
TextColor
.
ANSI
.
WHITE
textGraphics
.
backgroundColor
=
TextColor
.
ANSI
.
BLACK
textGraphics
.
putString
(
7
,
height
-
3
,
commandBuilder
.
toString
())
if
(
terminal
is
SwingTerminalFrame
)
{
terminal
.
flush
()
}
textGraphics
.
backgroundColor
=
TextColor
.
ANSI
.
DEFAULT
}
private
fun
addCommandChar
(
c
:
Char
)
{
if
(!
requesting
&&
commandBuilder
.
isEmpty
()
&&
c
!=
'/'
)
{
addCommandChar
(
'/'
)
}
textGraphics
.
foregroundColor
=
TextColor
.
ANSI
.
WHITE
textGraphics
.
backgroundColor
=
TextColor
.
ANSI
.
BLACK
val
height
=
terminal
.
terminalSize
.
rows
commandBuilder
.
append
(
c
)
if
(
terminal
is
SwingTerminalFrame
)
{
redrawCommand
()
}
else
{
textGraphics
.
putString
(
6
+
commandBuilder
.
length
,
height
-
3
,
c
.
toString
())
}
textGraphics
.
backgroundColor
=
TextColor
.
ANSI
.
DEFAULT
}
private
fun
deleteCommandChar
()
{
if
(!
commandBuilder
.
isEmpty
())
{
commandBuilder
=
StringBuilder
(
commandBuilder
.
toString
().
substring
(
0
,
commandBuilder
.
length
-
1
))
}
val
height
=
terminal
.
terminalSize
.
rows
if
(
terminal
is
SwingTerminalFrame
)
{
redrawCommand
()
}
else
{
textGraphics
.
putString
(
7
+
commandBuilder
.
length
,
height
-
3
,
" "
)
}
}
var
lastEmpty
:
Job
?
=
null
private
fun
emptyCommand
()
{
commandBuilder
=
StringBuilder
()
if
(
terminal
is
SwingTerminal
)
{
redrawCommand
()
terminal
.
flush
()
}
else
{
lastEmpty
=
GlobalScope
.
launch
{
try
{
delay
(
100
)
if
(
lastEmpty
==
coroutineContext
[
Job
])
{
terminal
.
clearScreen
()
//inited = false
update
()
redrawCommand
()
redrawLogs
(
log
[
screens
[
currentScreenId
]]
!!
)
}
}
catch
(
e
:
Exception
)
{
pushLog
(
0
,
"[UI ERROR] ${e.message}"
)
}
}
}
}
fun
update
()
{
when
(
screens
[
currentScreenId
])
{
0L
->
{
drawMainFrame
(
screens
.
size
-
1
)
}
else
->
{
drawBotFrame
(
screens
[
currentScreenId
],
0
)
}
}
redrawLogs
(
log
[
screens
[
currentScreenId
]]
!!
)
}
fun
exit
()
{
try
{
terminal
.
exitPrivateMode
()
terminal
.
close
()
exitProcess
(
0
)
}
catch
(
ignored
:
Exception
)
{
}
}
}
class
LimitLinkedQueue
<
T
>(
val
limit
:
Int
=
50
)
:
ConcurrentLinkedDeque
<
T
>()
{
override
fun
push
(
e
:
T
)
{
if
(
size
>=
limit
)
{
this
.
pollLast
()
}
return
super
.
push
(
e
)
}
}
mirai-console/README.MD
deleted
100644 → 0
View file @
6110cf8f
# Mirai Console
你可以在全平台运行Mirai高效率机器人框架
### Mirai Console提供了6个版本以满足各种需要
#### 所有版本的Mirai Console API相同 插件系统相同
| 名字 | 介绍 |
| --- | --- |
| Mirai-Console-Pure | 最纯净版, CLI环境, 通过标准输入与标准输出 交互 |
| Mirai-Console-Terminal | (UNIX)Terminal环境 提供简洁的富文本控制台 |
| Mirai-Console-Android | 安卓APP (TODO) |
| Mirai-Console-Graphical | JavaFX的图形化界面 (.jar/.exe/.dmg) |
| Mirai-Console-WebPanel | Web Panel操作(TODO) |
| Mirai-Console-Ios | IOS APP (TODO) |
### 如何选择版本
1: Mirai-Console-Pure 兼容性最高, 在其他都表现不佳的时候请使用
</br>
2: 以系统区分
```
kotlin
return
when
(
operatingSystem
){
WINDOWS
->
listOf
(
"Graphical"
,
"WebPanel"
,
"Pure"
)
MAC_OS
->
listOf
(
"Graphical"
,
"Terminal"
,
"WebPanel"
,
"Pure"
)
LINUX
->
listOf
(
"Terminal"
,
"Pure"
)
ANDROID
->
listOf
(
"Android"
,
"Pure"
,
"WebPanel"
)
IOS
->
listOf
(
"Ios"
)
else
->
listOf
(
"Pure"
)
}
```
3: 以策略区分
```
kotlin
return
when
(
task
){
体验
->
listOf
(
"Graphical"
,
"Terminal"
,
"WebPanel"
,
"Android"
,
"Pure"
)
测试插件
->
listOf
(
"Pure"
)
调试插件
->
byOperatingSystem
()
稳定挂机
->
listOf
(
"Terminal"
,
"Pure"
)
else
->
listOf
(
"Pure"
)
}
```
#### More Importantly, Mirai Console support <b>Plugins</b>, tells the bot what to do
#### Mirai Console 支持插件系统, 你可以自己开发或使用公开的插件来逻辑化机器人, 如群管
<br>
#### download 下载
#### how to get/write plugins 如何获取/写插件
<br>
<br>
### how to use(如何使用)
#### how to run Mirai Console
<ul>
<li>
download mirai-console.jar
</li>
<li>
open command line/terminal
</li>
<li>
create a folder and put mirai-console.jar in
</li>
<li>
cd that folder
</li>
<li>
"java -jar mirai-console.jar"
</li>
</ul>
<ul>
<li>
下载mirai-console.jar
</li>
<li>
打开终端
</li>
<li>
在任何地方创建一个文件夹, 并放入mirai-console.jar
</li>
<li>
在终端中打开该文件夹"cd"
</li>
<li>
输入"java -jar mirai-console.jar"
</li>
</ul>
#### how to add plugins
<ul>
<li>
After first time of running mirai console
</li>
<li>
/plugins/folder will be created next to mirai-console.jar
</li>
<li>
put plugin(.jar) into /plugins/
</li>
<li>
restart mirai console
</li>
<li>
checking logger and check if the plugin is loaded successfully
</li>
<li>
if the plugin has it own Config file, it normally appears in /plugins/{pluginName}/
</li>
</ul>
<ul>
<li>
在首次运行mirai console后
</li>
<li>
mirai-console.jar 的同级会出现/plugins/文件夹
</li>
<li>
将插件(.jar)放入/plugins/文件夹
</li>
<li>
重启mirai console
</li>
<li>
在开启后检查日志, 是否成功加载
</li>
<li>
如该插件有配置文件, 配置文件一般会创建在/plugins/插件名字/ 文件夹下
</li>
</ul>
mirai-console/build.gradle.kts
deleted
100644 → 0
View file @
6110cf8f
plugins
{
id
(
"kotlinx-serialization"
)
id
(
"kotlin"
)
id
(
"java"
)
}
apply
(
plugin
=
"com.github.johnrengelman.shadow"
)
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
kotlinx
(
id
:
String
,
version
:
String
)
=
"org.jetbrains.kotlinx:kotlinx-$id:$version"
fun
ktor
(
id
:
String
,
version
:
String
)
=
"io.ktor:ktor-$id:$version"
tasks
.
withType
<
com
.
github
.
jengelman
.
gradle
.
plugins
.
shadow
.
tasks
.
ShadowJar
>()
{
manifest
{
attributes
[
"Main-Class"
]
=
"net.mamoe.mirai.console.pure.MiraiConsolePureLoader"
}
}
dependencies
{
api
(
project
(
":mirai-core"
))
api
(
project
(
":mirai-core-qqandroid"
))
// api(project(":mirai-api-http"))
runtimeOnly
(
files
(
"../mirai-core-qqandroid/build/classes/kotlin/jvm/main"
))
runtimeOnly
(
files
(
"../mirai-core/build/classes/kotlin/jvm/main"
))
api
(
kotlin
(
"serialization"
))
api
(
group
=
"com.alibaba"
,
name
=
"fastjson"
,
version
=
"1.2.62"
)
api
(
group
=
"org.yaml"
,
name
=
"snakeyaml"
,
version
=
"1.25"
)
api
(
group
=
"com.moandjiezana.toml"
,
name
=
"toml4j"
,
version
=
"0.7.2"
)
api
(
"org.bouncycastle:bcprov-jdk15on:1.64"
)
implementation
(
"no.tornado:tornadofx:1.7.19"
)
// classpath is not set correctly by IDE
}
\ No newline at end of file
mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsole.kt
deleted
100644 → 0
View file @
6110cf8f
/*
* 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
*/
package
net.mamoe.mirai.console
import
kotlinx.coroutines.*
import
kotlinx.coroutines.channels.Channel
import
net.mamoe.mirai.Bot
import
net.mamoe.mirai.console.MiraiConsole.CommandProcessor.processNextCommandLine
import
net.mamoe.mirai.console.command.CommandManager
import
net.mamoe.mirai.console.command.CommandSender
import
net.mamoe.mirai.console.command.ConsoleCommandSender
import
net.mamoe.mirai.console.command.DefaultCommands
import
net.mamoe.mirai.console.plugins.PluginManager
import
net.mamoe.mirai.console.plugins.loadAsConfig
import
net.mamoe.mirai.console.plugins.withDefaultWrite
import
net.mamoe.mirai.console.utils.MiraiConsoleUI
import
net.mamoe.mirai.utils.cryptor.ECDH
import
java.io.File
object
MiraiConsole
{
/**
* 发布的版本号 统一修改位置
*/
const
val
version
=
"0.1.0"
const
val
coreVersion
=
"v0.18.0"
const
val
build
=
"Alpha"
/**
* 获取从Console登陆上的Bot, Bots
* */
val
bots
get
()
=
Bot
.
instances
fun
getBotByUIN
(
uin
:
Long
):
Bot
?
{
bots
.
forEach
{
if
(
it
.
get
()
?.
uin
==
uin
)
{
return
it
.
get
()
}
}
return
null
}
/**
* PluginManager
*/
val
pluginManager
:
PluginManager
get
()
=
PluginManager
/**
* 与前端交互所使用的Logger
*/
var
logger
=
UIPushLogger
/**
* Console运行路径
*/
var
path
:
String
=
System
.
getProperty
(
"user.dir"
)
/**
* Console前端接口
*/
lateinit
var
frontEnd
:
MiraiConsoleUI
/**
* 启动Console
*/
var
start
=
false
fun
start
(
frontEnd
:
MiraiConsoleUI
)
{
if
(
start
)
{
return
}
start
=
true
/* 加载ECDH */
try
{
ECDH
()
}
catch
(
ignored
:
Exception
)
{
}
//Security.removeProvider("BC")
/* 初始化前端 */
this
.
frontEnd
=
frontEnd
frontEnd
.
pushVersion
(
version
,
build
,
coreVersion
)
logger
(
"Mirai-console [$version $build | core version $coreVersion] is still in testing stage, major features are available"
)
logger
(
"Mirai-console now running under $path"
)
logger
(
"Get news in github: https://github.com/mamoe/mirai"
)
logger
(
"Mirai为开源项目,请自觉遵守开源项目协议"
)
logger
(
"Powered by Mamoe Technologies and contributors"
)
/* 依次启用功能 */
DefaultCommands
()
HTTPAPIAdaptar
()
pluginManager
.
loadPlugins
()
CommandProcessor
.
start
()
/* 通知启动完成 */
logger
(
"Mirai-console 启动完成"
)
logger
(
"\"/login qqnumber qqpassword \" to login a bot"
)
logger
(
"\"/login qq号 qq密码 \" 来登录一个BOT"
)
}
fun
stop
()
{
PluginManager
.
disableAllPlugins
()
try
{
bots
.
forEach
{
it
.
get
()
?.
close
()
}
}
catch
(
ignored
:
Exception
)
{
}
}
object
CommandProcessor
:
Job
by
{
GlobalScope
.
launch
(
start
=
CoroutineStart
.
LAZY
)
{
processNextCommandLine
()
}
}()
{
internal
class
FullCommand
(
val
sender
:
CommandSender
,
val
commandStr
:
String
)
private
val
commandChannel
:
Channel
<
FullCommand
>
=
Channel
()
suspend
fun
runConsoleCommand
(
command
:
String
)
{
commandChannel
.
send
(
FullCommand
(
ConsoleCommandSender
,
command
)
)
}
suspend
fun
runCommand
(
sender
:
CommandSender
,
command
:
String
)
{
commandChannel
.
send
(
FullCommand
(
sender
,
command
)
)
}
fun
runConsoleCommandBlocking
(
command
:
String
)
=
runBlocking
{
runConsoleCommand
(
command
)
}
fun
runCommandBlocking
(
sender
:
CommandSender
,
command
:
String
)
=
runBlocking
{
runCommand
(
sender
,
command
)
}
private
suspend
fun
processNextCommandLine
()
{
for
(
command
in
commandChannel
)
{
var
commandStr
=
command
.
commandStr
if
(!
commandStr
.
startsWith
(
"/"
))
{
commandStr
=
"/$commandStr"
}
if
(!
CommandManager
.
runCommand
(
command
.
sender
,
commandStr
))
{
command
.
sender
.
sendMessage
(
"未知指令 $commandStr"
)
}
}
}
}
object
UIPushLogger
{
operator
fun
invoke
(
any
:
Any
?
=
null
)
{
invoke
(
"[Mirai$version $build]"
,
0L
,
any
)
}
operator
fun
invoke
(
identityStr
:
String
,
identity
:
Long
,
any
:
Any
?
=
null
)
{
if
(
any
!=
null
)
{
frontEnd
.
pushLog
(
identity
,
"$identityStr: $any"
)
}
}
}
}
object
MiraiProperties
{
var
config
=
File
(
"${MiraiConsole.path}/mirai.properties"
).
loadAsConfig
()
var
HTTP_API_ENABLE
:
Boolean
by
config
.
withDefaultWrite
{
true
}
var
HTTP_API_PORT
:
Int
by
config
.
withDefaultWrite
{
8080
}
/*
var HTTP_API_AUTH_KEY: String by config.withDefaultWriteSave {
"InitKey" + generateSessionKey()
}*/
}
object
HTTPAPIAdaptar
{
operator
fun
invoke
()
{
/*
if (MiraiProperties.HTTP_API_ENABLE) {
if (MiraiProperties.HTTP_API_AUTH_KEY.startsWith("InitKey")) {
MiraiConsole.logger("请尽快更改初始生成的HTTP API AUTHKEY")
}
MiraiConsole.logger("正在启动HTTPAPI; 端口=" + MiraiProperties.HTTP_API_PORT)
MiraiHttpAPIServer.logger = SimpleLogger("HTTP API") { _, message, e ->
MiraiConsole.logger("[Mirai HTTP API]", 0, message)
}
MiraiHttpAPIServer.start(
MiraiProperties.HTTP_API_PORT,
MiraiProperties.HTTP_API_AUTH_KEY
)
MiraiConsole.logger("HTTPAPI启动完成; 端口= " + MiraiProperties.HTTP_API_PORT)
}*/
}
}
mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/Command.kt
deleted
100644 → 0
View file @
6110cf8f
/*
* 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
*/
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.contact.Contact
import
net.mamoe.mirai.contact.Group
import
net.mamoe.mirai.contact.sendMessage
import
net.mamoe.mirai.message.GroupMessage
import
net.mamoe.mirai.message.MessageReceipt
import
net.mamoe.mirai.message.data.MessageChain
import
net.mamoe.mirai.message.data.quote
import
net.mamoe.mirai.message.data.toMessage
import
net.mamoe.mirai.utils.MiraiExperimentalAPI
import
java.lang.StringBuilder
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
,
args
)
}
else
{
sender
.
sendMessage
(
this
.
usage
)
}
}
catch
(
e
:
Exception
)
{
sender
.
sendMessage
(
"在运行指令时出现了未知错误"
)
e
.
printStackTrace
()
}
finally
{
(
sender
as
CommandSenderImpl
).
flushMessage
()
}
}
return
true
}
}
interface
CommandSender
{
/**
* 立刻发送一条Message
*/
suspend
fun
sendMessage
(
messageChain
:
MessageChain
)
suspend
fun
sendMessage
(
message
:
String
)
/**
* 写入要发送的内容 所有内容最后会被以一条发出, 不管成功与否
*/
fun
appendMessage
(
message
:
String
)
fun
sendMessageBlocking
(
messageChain
:
MessageChain
)
=
runBlocking
{
sendMessage
(
messageChain
)
}
fun
sendMessageBlocking
(
message
:
String
)
=
runBlocking
{
sendMessage
(
message
)
}
}
abstract
class
CommandSenderImpl
:
CommandSender
{
internal
val
builder
=
StringBuilder
()
override
fun
appendMessage
(
message
:
String
)
{
builder
.
append
(
message
).
append
(
"\n"
)
}
internal
open
suspend
fun
flushMessage
()
{
if
(!
builder
.
isEmpty
())
{
sendMessage
(
builder
.
toString
().
removeSuffix
(
"\n"
))
}
}
}
object
ConsoleCommandSender
:
CommandSenderImpl
()
{
override
suspend
fun
sendMessage
(
messageChain
:
MessageChain
)
{
MiraiConsole
.
logger
(
"[Command]"
,
0
,
messageChain
.
toString
())
}
override
suspend
fun
sendMessage
(
message
:
String
)
{
MiraiConsole
.
logger
(
"[Command]"
,
0
,
message
)
}
override
suspend
fun
flushMessage
()
{
super
.
flushMessage
()
builder
.
clear
()
}
}
open
class
ContactCommandSender
(
val
contact
:
Contact
)
:
CommandSenderImpl
()
{
override
suspend
fun
sendMessage
(
messageChain
:
MessageChain
)
{
contact
.
sendMessage
(
messageChain
)
}
override
suspend
fun
sendMessage
(
message
:
String
)
{
contact
.
sendMessage
(
message
)
}
}
/**
* 弃用中
* */
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
>
val
description
:
String
val
usage
:
String
suspend
fun
onCommand
(
sender
:
CommandSender
,
args
:
List
<
String
>):
Boolean
fun
register
()
}
abstract
class
BlockingCommand
(
override
val
name
:
String
,
override
val
alias
:
List
<
String
>
=
listOf
(),
override
val
description
:
String
=
""
,
override
val
usage
:
String
=
""
)
:
Command
{
/**
* 最高优先级监听器
* 如果 return `false` 这次指令不会被 [PluginBase] 的全局 onCommand 监听器监听
* */
final
override
suspend
fun
onCommand
(
sender
:
CommandSender
,
args
:
List
<
String
>):
Boolean
{
return
withContext
(
Dispatchers
.
IO
)
{
onCommandBlocking
(
sender
,
args
)
}
}
abstract
fun
onCommandBlocking
(
sender
:
CommandSender
,
args
:
List
<
String
>):
Boolean
override
fun
register
()
{
CommandManager
.
register
(
this
)
}
}
class
AnonymousCommand
internal
constructor
(
override
val
name
:
String
,
override
val
alias
:
List
<
String
>,
override
val
description
:
String
,
override
val
usage
:
String
=
""
,
val
onCommand
:
suspend
CommandSender
.(
args
:
List
<
String
>)
->
Boolean
)
:
Command
{
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
()
{
var
name
:
String
?
=
null
var
alias
:
List
<
String
>?
=
null
var
description
:
String
=
""
var
usage
:
String
=
"use /help for help"
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
()
}
}
}
fun
registerCommand
(
builder
:
CommandBuilder
.()
->
Unit
):
Command
{
return
CommandBuilder
().
apply
(
builder
).
register
()
}
mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/DefaultCommands.kt
deleted
100644 → 0
View file @
6110cf8f
package
net.mamoe.mirai.console.command
import
kotlinx.coroutines.runBlocking
import
net.mamoe.mirai.Bot
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.removeManager
import
net.mamoe.mirai.contact.sendMessage
import
net.mamoe.mirai.event.subscribeMessages
import
net.mamoe.mirai.message.GroupMessage
import
net.mamoe.mirai.utils.SimpleLogger
import
java.lang.StringBuilder
import
java.util.NoSuchElementException
/**
* Some defaults commands are recommend to be replaced by plugin provided commands
*/
object
DefaultCommands
{
operator
fun
invoke
()
{
registerCommand
{
name
=
"manager"
description
=
"Add a manager"
onCommand
{
it
->
if
(
this
!
is
ConsoleCommandSender
)
{
sendMessage
(
"请在后台使用该指令"
)
return
@
onCommand
false
}
if
(
it
.
size
<
2
)
{
MiraiConsole
.
logger
(
"[Bot Manager]"
,
0
,
"/manager add [bot ID] [Manager ID]"
)
MiraiConsole
.
logger
(
"[Bot Manager]"
,
0
,
"/manager remove [bot ID] [Manager ID]"
)
MiraiConsole
.
logger
(
"[Bot Manager]"
,
0
,
"/manager list [bot ID]"
)
return
@
onCommand
true
}
val
botId
=
try
{
it
[
1
].
toLong
()
}
catch
(
e
:
Exception
)
{
MiraiConsole
.
logger
(
"[Bot Manager]"
,
0
,
it
[
1
]
+
" 不是一个Bot的ID"
)
return
@
onCommand
false
}
val
bot
=
MiraiConsole
.
getBotByUIN
(
botId
)
if
(
bot
==
null
)
{
MiraiConsole
.
logger
(
"[Bot Manager]"
,
0
,
"$botId 没有在Console中登陆"
)
return
@
onCommand
false
}
when
(
it
[
0
])
{
"add"
->
{
if
(
it
.
size
<
3
)
{
MiraiConsole
.
logger
(
"[Bot Manager]"
,
0
,
"/manager add [bot ID] [Manager ID]"
)
return
@
onCommand
true
}
val
adminID
=
try
{
it
[
2
].
toLong
()
}
catch
(
e
:
Exception
)
{
MiraiConsole
.
logger
(
"[Bot Manager]"
,
0
,
it
[
2
]
+
" 不是一个ID"
)
return
@
onCommand
false
}
bot
.
addManager
(
adminID
)
MiraiConsole
.
logger
(
"[Bot Manager]"
,
0
,
it
[
2
]
+
"增加成功"
)
}
"remove"
->
{
if
(
it
.
size
<
3
)
{
MiraiConsole
.
logger
(
"[Bot Manager]"
,
0
,
"/manager remove [bot ID] [Manager ID]"
)
return
@
onCommand
true
}
val
adminID
=
try
{
it
[
2
].
toLong
()
}
catch
(
e
:
Exception
)
{
MiraiConsole
.
logger
(
"[Bot Manager]"
,
0
,
it
[
1
]
+
" 不是一个ID"
)
return
@
onCommand
false
}
if
(!
bot
.
checkManager
(
adminID
))
{
MiraiConsole
.
logger
(
"[Bot Manager]"
,
0
,
it
[
2
]
+
"本身不是一个Manager"
)
return
@
onCommand
true
}
bot
.
removeManager
(
adminID
)
MiraiConsole
.
logger
(
"[Bot Manager]"
,
0
,
it
[
2
]
+
"移除成功"
)
}
"list"
->
{
bot
.
getManagers
().
forEach
{
MiraiConsole
.
logger
(
"[Bot Manager]"
,
0
,
" -> $it"
)
}
}
}
return
@
onCommand
true
}
}
registerCommand
{
name
=
"login"
description
=
"机器人登陆"
onCommand
{
if
(
this
!
is
ConsoleCommandSender
)
{
sendMessage
(
"请在后台使用该指令"
)
return
@
onCommand
false
}
if
(
it
.
size
<
2
)
{
MiraiConsole
.
logger
(
"\"/login qqnumber qqpassword \" to login a bot"
)
MiraiConsole
.
logger
(
"\"/login qq号 qq密码 \" 来登录一个BOT"
)
return
@
onCommand
false
}
val
qqNumber
=
it
[
0
].
toLong
()
val
qqPassword
=
it
[
1
]
MiraiConsole
.
logger
(
"[Bot Login]"
,
0
,
"login..."
)
try
{
MiraiConsole
.
frontEnd
.
prePushBot
(
qqNumber
)
val
bot
=
Bot
(
qqNumber
,
qqPassword
)
{
this
.
loginSolver
=
MiraiConsole
.
frontEnd
.
createLoginSolver
()
this
.
botLoggerSupplier
=
{
SimpleLogger
(
"BOT $qqNumber]"
)
{
_
,
message
,
e
->
MiraiConsole
.
logger
(
"[BOT $qqNumber]"
,
qqNumber
,
message
)
if
(
e
!=
null
)
{
MiraiConsole
.
logger
(
"[NETWORK ERROR]"
,
qqNumber
,
e
.
toString
())
//因为在一页 所以可以不打QQ
e
.
printStackTrace
()
}
}
}
this
.
networkLoggerSupplier
=
{
SimpleLogger
(
"BOT $qqNumber"
)
{
_
,
message
,
e
->
MiraiConsole
.
logger
(
"[NETWORK]"
,
qqNumber
,
message
)
//因为在一页 所以可以不打QQ
if
(
e
!=
null
)
{
MiraiConsole
.
logger
(
"[NETWORK ERROR]"
,
qqNumber
,
e
.
toString
())
//因为在一页 所以可以不打QQ
e
.
printStackTrace
()
}
}
}
}
bot
.
login
()
bot
.
subscribeMessages
{
this
.
startsWith
(
"/"
)
{
if
(
bot
.
checkManager
(
this
.
sender
.
id
))
{
val
sender
=
ContactCommandSender
(
this
.
subject
)
MiraiConsole
.
CommandProcessor
.
runCommand
(
sender
,
it
)
}
}
}
sendMessage
(
"$qqNumber login successes"
)
MiraiConsole
.
frontEnd
.
pushBot
(
bot
)
}
catch
(
e
:
Exception
)
{
sendMessage
(
"$qqNumber login failed -> "
+
e
.
message
)
}
true
}
}
registerCommand
{
name
=
"status"
description
=
"获取状态"
onCommand
{
when
(
it
.
size
)
{
0
->
{
sendMessage
(
"当前有"
+
MiraiConsole
.
bots
.
size
+
"个BOT在线"
)
}
1
->
{
val
bot
=
it
[
0
]
var
find
=
false
MiraiConsole
.
bots
.
forEach
{
if
(
it
.
get
()
?.
uin
.
toString
().
contains
(
bot
))
{
find
=
true
appendMessage
(
""
+
it
.
get
()
?.
uin
+
": 在线中; 好友数量:"
+
it
.
get
()
?.
qqs
?.
size
+
"; 群组数量:"
+
it
.
get
()
?.
groups
?.
size
)
}
}
if
(!
find
)
{
sendMessage
(
"没有找到BOT$bot"
)
}
}
}
true
}
}
registerCommand
{
name
=
"say"
description
=
"聊天功能演示"
onCommand
{
if
(
it
.
size
<
2
)
{
MiraiConsole
.
logger
(
"say [好友qq号或者群号] [文本消息] //将默认使用第一个BOT"
)
MiraiConsole
.
logger
(
"say [bot号] [好友qq号或者群号] [文本消息]"
)
return
@
onCommand
false
}
val
bot
:
Bot
?
=
if
(
it
.
size
==
2
)
{
if
(
MiraiConsole
.
bots
.
size
==
0
)
{
MiraiConsole
.
logger
(
"还没有BOT登录"
)
return
@
onCommand
false
}
MiraiConsole
.
bots
[
0
].
get
()
}
else
{
MiraiConsole
.
getBotByUIN
(
it
[
0
].
toLong
())
}
if
(
bot
==
null
)
{
MiraiConsole
.
logger
(
"没有找到BOT"
)
return
@
onCommand
false
}
val
target
=
it
[
it
.
size
-
2
].
toLong
()
val
message
=
it
[
it
.
size
-
1
]
try
{
val
contact
=
bot
[
target
]
runBlocking
{
contact
.
sendMessage
(
message
)
MiraiConsole
.
logger
(
"消息已推送"
)
}
}
catch
(
e
:
NoSuchElementException
)
{
MiraiConsole
.
logger
(
"没有找到群或好友 号码为${target}"
)
return
@
onCommand
false
}
true
}
}
registerCommand
{
name
=
"plugins"
alias
=
listOf
(
"plugin"
)
description
=
"获取插件列表"
onCommand
{
PluginManager
.
getAllPluginDescriptions
().
let
{
it
.
forEach
{
appendMessage
(
"\t"
+
it
.
name
+
" v"
+
it
.
version
+
" by"
+
it
.
author
+
" "
+
it
.
info
)
}
appendMessage
(
"加载了"
+
it
.
size
+
"个插件"
)
true
}
}
}
registerCommand
{
name
=
"command"
alias
=
listOf
(
"commands"
,
"help"
,
"helps"
)
description
=
"获取指令列表"
onCommand
{
CommandManager
.
getCommands
().
let
{
var
size
=
0
appendMessage
(
""
)
//\n
it
.
toSet
().
forEach
{
++
size
appendMessage
(
"-> "
+
it
.
name
+
" :"
+
it
.
description
)
}
appendMessage
(
"""共有${size}条指令"""
)
}
true
}
}
registerCommand
{
name
=
"about"
description
=
"About Mirai-Console"
onCommand
{
appendMessage
(
"v${MiraiConsole.version} ${MiraiConsole.build} is still in testing stage, major features are available"
)
appendMessage
(
"now running under ${MiraiConsole.path}"
)
appendMessage
(
"在Github中获取项目最新进展: https://github.com/mamoe/mirai"
)
appendMessage
(
"Mirai为开源项目,请自觉遵守开源项目协议"
)
appendMessage
(
"Powered by Mamoe Technologies and contributors"
)
true
}
}
}
}
\ No newline at end of file
mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/ConfigSection.kt
deleted
100644 → 0
View file @
6110cf8f
/*
* 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
*/
package
net.mamoe.mirai.console.plugins
import
com.alibaba.fastjson.JSON
import
com.alibaba.fastjson.JSONObject
import
com.alibaba.fastjson.TypeReference
import
com.alibaba.fastjson.parser.Feature
import
com.moandjiezana.toml.Toml
import
com.moandjiezana.toml.TomlWriter
import
kotlinx.serialization.Serializable
import
kotlinx.serialization.UnstableDefault
import
net.mamoe.mirai.utils.io.encodeToString
import
org.yaml.snakeyaml.Yaml
import
tornadofx.c
import
java.io.File
import
java.io.InputStream
import
java.util.*
import
java.util.concurrent.ConcurrentHashMap
import
kotlin.collections.LinkedHashMap
import
kotlin.properties.ReadWriteProperty
import
kotlin.reflect.KClass
import
kotlin.reflect.KProperty
import
kotlin.reflect.full.isSubclassOf
/**
* TODO: support all config types
* only JSON is now supported
*
*/
interface
Config
{
fun
getConfigSection
(
key
:
String
):
ConfigSection
fun
getString
(
key
:
String
):
String
fun
getInt
(
key
:
String
):
Int
fun
getFloat
(
key
:
String
):
Float
fun
getDouble
(
key
:
String
):
Double
fun
getLong
(
key
:
String
):
Long
fun
getBoolean
(
key
:
String
):
Boolean
fun
getList
(
key
:
String
):
List
<
*
>
fun
getStringList
(
key
:
String
):
List
<
String
>
fun
getIntList
(
key
:
String
):
List
<
Int
>
fun
getFloatList
(
key
:
String
):
List
<
Float
>
fun
getDoubleList
(
key
:
String
):
List
<
Double
>
fun
getLongList
(
key
:
String
):
List
<
Long
>
fun
getConfigSectionList
(
key
:
String
):
List
<
ConfigSection
>
operator
fun
set
(
key
:
String
,
value
:
Any
)
operator
fun
get
(
key
:
String
):
Any
?
operator
fun
contains
(
key
:
String
):
Boolean
fun
exist
(
key
:
String
):
Boolean
fun
setIfAbsent
(
key
:
String
,
value
:
Any
)
fun
asMap
():
Map
<
String
,
Any
>
fun
save
()
companion
object
{
fun
load
(
fileName
:
String
):
Config
{
return
load
(
File
(
fileName
.
replace
(
"//"
,
"/"
)
)
)
}
/**
* create a read-write config
* */
fun
load
(
file
:
File
):
Config
{
if
(!
file
.
exists
())
{
file
.
createNewFile
()
}
return
when
(
file
.
extension
.
toLowerCase
())
{
"json"
->
JsonConfig
(
file
)
"yml"
->
YamlConfig
(
file
)
"yaml"
->
YamlConfig
(
file
)
"mirai"
->
YamlConfig
(
file
)
"ini"
->
TomlConfig
(
file
)
"toml"
->
TomlConfig
(
file
)
"properties"
->
TomlConfig
(
file
)
"property"
->
TomlConfig
(
file
)
"data"
->
TomlConfig
(
file
)
else
->
error
(
"Unsupported file config type ${file.extension.toLowerCase()}"
)
}
}
/**
* create a read-only config
*/
fun
load
(
content
:
String
,
type
:
String
):
Config
{
return
when
(
type
.
toLowerCase
())
{
"json"
->
JsonConfig
(
content
)
"yml"
->
YamlConfig
(
content
)
"yaml"
->
YamlConfig
(
content
)
"mirai"
->
YamlConfig
(
content
)
"ini"
->
TomlConfig
(
content
)
"toml"
->
TomlConfig
(
content
)
"properties"
->
TomlConfig
(
content
)
"property"
->
TomlConfig
(
content
)
"data"
->
TomlConfig
(
content
)
else
->
error
(
"Unsupported file config type $content"
)
}
}
/**
* create a read-only config
*/
fun
load
(
inputStream
:
InputStream
,
type
:
String
):
Config
{
return
load
(
inputStream
.
readBytes
().
encodeToString
(),
type
)
}
}
}
fun
File
.
loadAsConfig
():
Config
{
return
Config
.
load
(
this
)
}
/* 最简单的代理 */
inline
operator
fun
<
reified
T
:
Any
>
Config
.
getValue
(
thisRef
:
Any
?,
property
:
KProperty
<
*
>):
T
{
return
smartCast
(
property
)
}
inline
operator
fun
<
reified
T
:
Any
>
Config
.
setValue
(
thisRef
:
Any
?,
property
:
KProperty
<
*
>,
value
:
T
)
{
this
[
property
.
name
]
=
value
}
/* 带有默认值的代理 */
inline
fun
<
reified
T
:
Any
>
Config
.
withDefault
(
noinline
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
}
override
fun
setValue
(
thisRef
:
Any
,
property
:
KProperty
<
*
>,
value
:
T
)
{
this
@
withDefault
[
property
.
name
]
=
value
}
}
}
/* 带有默认值且如果为空会写入的代理 */
inline
fun
<
reified
T
:
Any
>
Config
.
withDefaultWrite
(
noinline
defaultValue
:
()
->
T
):
WithDefaultWriteLoader
<
T
>
{
return
WithDefaultWriteLoader
(
T
::
class
,
this
,
defaultValue
,
false
)
}
/* 带有默认值且如果为空会写入保存的代理 */
inline
fun
<
reified
T
:
Any
>
Config
.
withDefaultWriteSave
(
noinline
defaultValue
:
()
->
T
):
WithDefaultWriteLoader
<
T
>
{
return
WithDefaultWriteLoader
(
T
::
class
,
this
,
defaultValue
,
true
)
}
class
WithDefaultWriteLoader
<
T
:
Any
>(
private
val
_class
:
KClass
<
T
>,
private
val
config
:
Config
,
private
val
defaultValue
:
()
->
T
,
private
val
save
:
Boolean
)
{
operator
fun
provideDelegate
(
thisRef
:
Any
,
prop
:
KProperty
<
*
>
):
ReadWriteProperty
<
Any
,
T
>
{
val
defaultValue
by
lazy
{
defaultValue
.
invoke
()
}
if
(!
config
.
contains
(
prop
.
name
))
{
config
[
prop
.
name
]
=
defaultValue
if
(
save
)
{
config
.
save
()
}
}
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
defaultValue
}
override
fun
setValue
(
thisRef
:
Any
,
property
:
KProperty
<
*
>,
value
:
T
)
{
config
[
property
.
name
]
=
value
}
}
}
}
inline
fun
<
reified
T
:
Any
>
Config
.
smartCast
(
property
:
KProperty
<
*
>):
T
{
return
_smartCast
(
property
.
name
,
T
::
class
)
}
@Suppress
(
"IMPLICIT_CAST_TO_ANY"
,
"UNCHECKED_CAST"
)
fun
<
T
:
Any
>
Config
.
_smartCast
(
propertyName
:
String
,
_class
:
KClass
<
T
>):
T
{
return
when
(
_class
)
{
String
::
class
-> this.get
String
(
propertyName
)
Int
::
class
-> this.get
Int
(
propertyName
)
Float
::
class
-> this.get
Float
(
propertyName
)
Double
::
class
-> this.get
Double
(
propertyName
)
Long
::
class
-> this.get
Long
(
propertyName
)
Boolean
::
class
-> this.get
Boolean
(
propertyName
)
else
->
when
{
_class
.
isSubclassOf
(
ConfigSection
::
class
)
->
this
.
getConfigSection
(
propertyName
)
_class
==
List
::
class
|| _class ==
MutableList
::
class
-> {
val list = this.get
List
(
propertyName
)
return
if
(
list
.
isEmpty
())
{
list
}
else
{
when
(
list
[
0
]
!!::
class
)
{
String
::
class
-> get
StringList
(
propertyName
)
Int
::
class
-> get
IntList
(
propertyName
)
Float
::
class
-> get
FloatList
(
propertyName
)
Double
::
class
-> get
DoubleList
(
propertyName
)
Long
::
class
-> get
LongList
(
propertyName
)
//不去支持getConfigSectionList(propertyName)
// LinkedHashMap::class -> getConfigSectionList(propertyName)//faster approach
else
->
{
//if(list[0]!! is ConfigSection || list[0]!! is Map<*,*>){
// getConfigSectionList(propertyName)
//}else {
error
(
"unsupported type"
+
list
[
0
]
!!::
class
)
//}
}
}
}
as
T
}
else
->
{
error
(
"unsupported type"
)
}
}
}
as
T
}
interface
ConfigSection
:
Config
,
MutableMap
<
String
,
Any
>
{
override
fun
getConfigSection
(
key
:
String
):
ConfigSection
{
val
content
=
get
(
key
)
?:
error
(
"ConfigSection does not contain $key "
)
if
(
content
is
ConfigSection
)
{
return
content
}
return
ConfigSectionDelegation
(
Collections
.
synchronizedMap
(
(
get
(
key
)
?:
error
(
"ConfigSection does not contain $key "
))
as
LinkedHashMap
<
String
,
Any
>
)
)
}
override
fun
getString
(
key
:
String
):
String
{
return
(
get
(
key
)
?:
error
(
"ConfigSection does not contain $key "
)).
toString
()
}
override
fun
getInt
(
key
:
String
):
Int
{
return
(
get
(
key
)
?:
error
(
"ConfigSection does not contain $key "
)).
toString
().
toInt
()
}
override
fun
getFloat
(
key
:
String
):
Float
{
return
(
get
(
key
)
?:
error
(
"ConfigSection does not contain $key "
)).
toString
().
toFloat
()
}
override
fun
getBoolean
(
key
:
String
):
Boolean
{
return
(
get
(
key
)
?:
error
(
"ConfigSection does not contain $key "
)).
toString
().
toBoolean
()
}
override
fun
getDouble
(
key
:
String
):
Double
{
return
(
get
(
key
)
?:
error
(
"ConfigSection does not contain $key "
)).
toString
().
toDouble
()
}
override
fun
getLong
(
key
:
String
):
Long
{
return
(
get
(
key
)
?:
error
(
"ConfigSection does not contain $key "
)).
toString
().
toLong
()
}
override
fun
getList
(
key
:
String
):
List
<
*
>
{
return
((
get
(
key
)
?:
error
(
"ConfigSection does not contain $key "
))
as
List
<
*
>)
}
override
fun
getStringList
(
key
:
String
):
List
<
String
>
{
return
((
get
(
key
)
?:
error
(
"ConfigSection does not contain $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
()
}
}
override
fun
getFloatList
(
key
:
String
):
List
<
Float
>
{
return
((
get
(
key
)
?:
error
(
"ConfigSection does not contain $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
()
}
}
override
fun
getLongList
(
key
:
String
):
List
<
Long
>
{
return
((
get
(
key
)
?:
error
(
"ConfigSection does not contain $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
{
if
(
it
is
ConfigSection
)
{
it
}
else
{
ConfigSectionDelegation
(
Collections
.
synchronizedMap
(
it
as
MutableMap
<
String
,
Any
>
)
)
}
}
}
override
fun
exist
(
key
:
String
):
Boolean
{
return
get
(
key
)
!=
null
}
override
fun
setIfAbsent
(
key
:
String
,
value
:
Any
)
{
if
(!
exist
(
key
))
set
(
key
,
value
)
}
}
@Serializable
open
class
ConfigSectionImpl
()
:
ConcurrentHashMap
<
String
,
Any
>(),
ConfigSection
{
override
fun
set
(
key
:
String
,
value
:
Any
)
{
super
.
put
(
key
,
value
)
}
override
operator
fun
get
(
key
:
String
):
Any
?
{
return
super
.
get
(
key
)
}
@Suppress
(
"RedundantOverride"
)
override
fun
contains
(
key
:
String
):
Boolean
{
return
super
.
contains
(
key
)
}
override
fun
exist
(
key
:
String
):
Boolean
{
return
containsKey
(
key
)
}
override
fun
asMap
():
Map
<
String
,
Any
>
{
return
this
}
override
fun
save
()
{
}
override
fun
setIfAbsent
(
key
:
String
,
value
:
Any
)
{
this
.
putIfAbsent
(
key
,
value
)
//atomic
}
}
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
)
}
override
fun
contains
(
key
:
String
):
Boolean
{
return
delegate
.
containsKey
(
key
)
}
override
fun
asMap
():
Map
<
String
,
Any
>
{
return
delegate
}
override
fun
save
()
{
}
}
interface
FileConfig
:
Config
{
fun
deserialize
(
content
:
String
):
ConfigSection
fun
serialize
(
config
:
ConfigSection
):
String
}
abstract
class
FileConfigImpl
internal
constructor
(
private
val
rawContent
:
String
)
:
FileConfig
,
ConfigSection
{
internal
var
file
:
File
?
=
null
constructor
(
file
:
File
)
:
this
(
file
.
readText
())
{
this
.
file
=
file
}
private
val
content
by
lazy
{
deserialize
(
rawContent
)
}
override
val
size
:
Int
get
()
=
content
.
size
override
val
entries
:
MutableSet
<
MutableMap
.
MutableEntry
<
String
,
Any
>>
get
()
=
content
.
entries
override
val
keys
:
MutableSet
<
String
>
get
()
=
content
.
keys
override
val
values
:
MutableCollection
<
Any
>
get
()
=
content
.
values
override
fun
containsKey
(
key
:
String
):
Boolean
=
content
.
containsKey
(
key
)
override
fun
containsValue
(
value
:
Any
):
Boolean
=
content
.
containsValue
(
value
)
override
fun
put
(
key
:
String
,
value
:
Any
):
Any
?
=
content
.
put
(
key
,
value
)
override
fun
isEmpty
():
Boolean
=
content
.
isEmpty
()
override
fun
putAll
(
from
:
Map
<
out
String
,
Any
>)
=
content
.
putAll
(
from
)
override
fun
clear
()
=
content
.
clear
()
override
fun
remove
(
key
:
String
):
Any
?
=
content
.
remove
(
key
)
override
fun
save
()
{
if
(
isReadOnly
())
{
error
(
"Config is readonly"
)
}
if
(!((
file
?.
exists
())
!!
))
{
file
?.
createNewFile
()
}
file
?.
writeText
(
serialize
(
content
))
}
fun
isReadOnly
()
=
file
==
null
override
fun
contains
(
key
:
String
):
Boolean
{
return
content
.
contains
(
key
)
}
override
fun
get
(
key
:
String
):
Any
?
{
return
content
[
key
]
}
override
fun
set
(
key
:
String
,
value
:
Any
)
{
content
[
key
]
=
value
}
override
fun
asMap
():
Map
<
String
,
Any
>
{
return
content
.
asMap
()
}
}
class
JsonConfig
internal
constructor
(
content
:
String
)
:
FileConfigImpl
(
content
)
{
constructor
(
file
:
File
)
:
this
(
file
.
readText
())
{
this
.
file
=
file
}
@UnstableDefault
override
fun
deserialize
(
content
:
String
):
ConfigSection
{
if
(
content
.
isEmpty
()
||
content
.
isBlank
()
||
content
==
"{}"
)
{
return
ConfigSectionImpl
()
}
return
JSON
.
parseObject
<
ConfigSectionImpl
>(
content
,
object
:
TypeReference
<
ConfigSectionImpl
>()
{},
Feature
.
OrderedField
)
}
@UnstableDefault
override
fun
serialize
(
config
:
ConfigSection
):
String
{
return
JSONObject
.
toJSONString
(
config
)
}
}
class
YamlConfig
internal
constructor
(
content
:
String
)
:
FileConfigImpl
(
content
)
{
constructor
(
file
:
File
)
:
this
(
file
.
readText
())
{
this
.
file
=
file
}
override
fun
deserialize
(
content
:
String
):
ConfigSection
{
if
(
content
.
isEmpty
()
||
content
.
isBlank
())
{
return
ConfigSectionImpl
()
}
return
ConfigSectionDelegation
(
Collections
.
synchronizedMap
(
Yaml
().
load
<
LinkedHashMap
<
String
,
Any
>>(
content
)
as
LinkedHashMap
<
String
,
Any
>
)
)
}
override
fun
serialize
(
config
:
ConfigSection
):
String
{
return
Yaml
().
dumpAsMap
(
config
)
}
}
class
TomlConfig
internal
constructor
(
content
:
String
)
:
FileConfigImpl
(
content
)
{
constructor
(
file
:
File
)
:
this
(
file
.
readText
())
{
this
.
file
=
file
}
override
fun
deserialize
(
content
:
String
):
ConfigSection
{
if
(
content
.
isEmpty
()
||
content
.
isBlank
())
{
return
ConfigSectionImpl
()
}
return
ConfigSectionDelegation
(
Collections
.
synchronizedMap
(
Toml
().
read
(
content
).
toMap
()
)
)
}
override
fun
serialize
(
config
:
ConfigSection
):
String
{
return
TomlWriter
().
write
(
config
)
}
}
\ No newline at end of file
mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginBase.kt
deleted
100644 → 0
View file @
6110cf8f
/*
* 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
*/
package
net.mamoe.mirai.console.plugins
import
kotlinx.coroutines.*
import
net.mamoe.mirai.console.MiraiConsole
import
net.mamoe.mirai.console.command.Command
import
net.mamoe.mirai.utils.DefaultLogger
import
net.mamoe.mirai.utils.MiraiLogger
import
net.mamoe.mirai.utils.SimpleLogger
import
net.mamoe.mirai.utils.io.encodeToString
import
java.io.File
import
java.io.InputStream
import
java.net.URL
import
java.net.URLClassLoader
import
java.util.jar.JarFile
import
kotlin.coroutines.CoroutineContext
import
kotlin.coroutines.EmptyCoroutineContext
abstract
class
PluginBase
(
coroutineContext
:
CoroutineContext
)
:
CoroutineScope
{
constructor
()
:
this
(
EmptyCoroutineContext
)
private
val
supervisorJob
=
SupervisorJob
()
final
override
val
coroutineContext
:
CoroutineContext
=
coroutineContext
+
supervisorJob
/**
* 插件被分配的data folder, 如果插件改名了 data folder 也会变 请注意
*/
val
dataFolder
:
File
by
lazy
{
File
(
PluginManager
.
pluginsPath
+
pluginDescription
.
name
).
also
{
it
.
mkdir
()
}
}
/**
* 当一个插件被加载时调用
*/
open
fun
onLoad
()
{
}
/**
* 当所有插件全部被加载后被调用
*/
open
fun
onEnable
()
{
}
/**
* 当插件关闭前被调用
*/
open
fun
onDisable
()
{
}
/**
* 当任意指令被使用
*/
open
fun
onCommand
(
command
:
Command
,
args
:
List
<
String
>)
{
}
internal
fun
enable
()
{
this
.
onEnable
()
}
/**
* 加载一个data folder中的Config
* 这个config是read-write的
*/
fun
loadConfig
(
fileName
:
String
):
Config
{
return
Config
.
load
(
dataFolder
.
absolutePath
+
fileName
)
}
@JvmOverloads
internal
fun
disable
(
throwable
:
CancellationException
?
=
null
)
{
this
.
coroutineContext
[
Job
]
!!
.
cancelChildren
(
throwable
)
this
.
onDisable
()
}
private
lateinit
var
pluginDescription
:
PluginDescription
internal
fun
init
(
pluginDescription
:
PluginDescription
)
{
this
.
pluginDescription
=
pluginDescription
this
.
onLoad
()
}
val
pluginManager
=
PluginManager
val
logger
:
MiraiLogger
by
lazy
{
SimpleLogger
(
"Plugin ${pluginDescription.name}"
)
{
_
,
message
,
e
->
MiraiConsole
.
logger
(
"[${pluginDescription.name}]"
,
0
,
message
)
if
(
e
!=
null
)
{
MiraiConsole
.
logger
(
"[${pluginDescription.name}]"
,
0
,
e
.
toString
())
e
.
printStackTrace
()
}
}
}
/**
* 加载一个插件jar, resources中的东西
*/
fun
getResources
(
fileName
:
String
):
InputStream
?
{
return
try
{
this
.
javaClass
.
classLoader
.
getResourceAsStream
(
fileName
)
}
catch
(
e
:
Exception
)
{
PluginManager
.
getFileInJarByName
(
this
.
pluginDescription
.
name
,
fileName
)
}
}
/**
* 加载一个插件jar, resources中的Config
* 这个Config是read-only的
*/
fun
getResourcesConfig
(
fileName
:
String
):
Config
{
if
(
fileName
.
contains
(
"."
))
{
error
(
"Unknown Config Type"
)
}
return
Config
.
load
(
getResources
(
fileName
)
?:
error
(
"Config Not Found"
),
fileName
.
split
(
"."
)[
1
])
}
}
class
PluginDescription
(
val
name
:
String
,
val
author
:
String
,
val
basePath
:
String
,
val
version
:
String
,
val
info
:
String
,
val
depends
:
List
<
String
>,
//插件的依赖
internal
var
loaded
:
Boolean
=
false
,
internal
var
noCircularDepend
:
Boolean
=
true
)
{
override
fun
toString
():
String
{
return
"name: $name\nauthor: $author\npath: $basePath\nver: $version\ninfo: $info\ndepends: $depends"
}
companion
object
{
fun
readFromContent
(
content_
:
String
):
PluginDescription
{
val
content
=
content_
.
split
(
"\n"
)
var
name
=
"Plugin"
var
author
=
"Unknown"
var
basePath
=
"net.mamoe.mirai.PluginMain"
var
info
=
"Unknown"
var
version
=
"1.0.0"
val
depends
=
mutableListOf
<
String
>();
content
.
forEach
{
val
line
=
it
.
trim
()
val
lowercaseLine
=
line
.
toLowerCase
()
if
(
it
.
contains
(
":"
))
{
when
{
lowercaseLine
.
startsWith
(
"name"
)
->
{
name
=
line
.
substringAfter
(
":"
).
trim
()
}
lowercaseLine
.
startsWith
(
"author"
)
->
{
author
=
line
.
substringAfter
(
":"
).
trim
()
}
lowercaseLine
.
startsWith
(
"info"
)
||
lowercaseLine
.
startsWith
(
"information"
)
->
{
info
=
line
.
substringAfter
(
":"
).
trim
()
}
lowercaseLine
.
startsWith
(
"main"
)
||
lowercaseLine
.
startsWith
(
"path"
)
||
lowercaseLine
.
startsWith
(
"basepath"
)
->
{
basePath
=
line
.
substringAfter
(
":"
).
trim
()
}
lowercaseLine
.
startsWith
(
"version"
)
||
lowercaseLine
.
startsWith
(
"ver"
)
->
{
version
=
line
.
substringAfter
(
":"
).
trim
()
}
}
}
else
if
(
line
.
startsWith
(
"-"
))
{
depends
.
add
(
line
.
substringAfter
(
"-"
).
trim
())
}
}
return
PluginDescription
(
name
,
author
,
basePath
,
version
,
info
,
depends
)
}
}
}
internal
class
PluginClassLoader
(
file
:
File
,
parent
:
ClassLoader
)
:
URLClassLoader
(
arrayOf
(
file
.
toURI
().
toURL
()),
parent
)
object
PluginManager
{
internal
val
pluginsPath
=
System
.
getProperty
(
"user.dir"
)
+
"/plugins/"
.
replace
(
"//"
,
"/"
).
also
{
File
(
it
).
mkdirs
()
}
val
logger
=
SimpleLogger
(
"Plugin Manager"
)
{
_
,
message
,
e
->
MiraiConsole
.
logger
(
"[Plugin Manager]"
,
0
,
message
)
}
//已完成加载的
private
val
nameToPluginBaseMap
:
MutableMap
<
String
,
PluginBase
>
=
mutableMapOf
()
private
val
pluginDescriptions
:
MutableMap
<
String
,
PluginDescription
>
=
mutableMapOf
()
fun
onCommand
(
command
:
Command
,
args
:
List
<
String
>)
{
nameToPluginBaseMap
.
values
.
forEach
{
it
.
onCommand
(
command
,
args
)
}
}
fun
getAllPluginDescriptions
():
Collection
<
PluginDescription
>
{
return
pluginDescriptions
.
values
}
/**
* 尝试加载全部插件
*/
fun
loadPlugins
()
{
val
pluginsFound
:
MutableMap
<
String
,
PluginDescription
>
=
mutableMapOf
()
val
pluginsLocation
:
MutableMap
<
String
,
File
>
=
mutableMapOf
()
logger
.
info
(
"""开始加载${pluginsPath}下的插件"""
)
File
(
pluginsPath
).
listFiles
()
?.
forEach
{
file
->
if
(
file
!=
null
&&
file
.
extension
==
"jar"
)
{
val
jar
=
JarFile
(
file
)
val
pluginYml
=
jar
.
entries
().
asSequence
().
filter
{
it
.
name
.
toLowerCase
().
contains
(
"plugin.yml"
)
}.
firstOrNull
()
if
(
pluginYml
==
null
)
{
logger
.
info
(
"plugin.yml not found in jar "
+
jar
.
name
+
", it will not be consider as a Plugin"
)
}
else
{
val
description
=
PluginDescription
.
readFromContent
(
URL
(
"jar:file:"
+
file
.
absoluteFile
+
"!/"
+
pluginYml
.
name
).
openConnection
().
inputStream
.
use
{
it
.
readBytes
().
encodeToString
()
})
pluginsFound
[
description
.
name
]
=
description
pluginsLocation
[
description
.
name
]
=
file
}
}
}
fun
checkNoCircularDepends
(
target
:
PluginDescription
,
needDepends
:
List
<
String
>,
existDepends
:
MutableList
<
String
>
)
{
if
(!
target
.
noCircularDepend
)
{
return
}
existDepends
.
add
(
target
.
name
)
if
(
needDepends
.
any
{
existDepends
.
contains
(
it
)
})
{
target
.
noCircularDepend
=
false
}
existDepends
.
addAll
(
needDepends
)
needDepends
.
forEach
{
if
(
pluginsFound
.
containsKey
(
it
))
{
checkNoCircularDepends
(
pluginsFound
[
it
]
!!
,
pluginsFound
[
it
]
!!
.
depends
,
existDepends
)
}
}
}
pluginsFound
.
values
.
forEach
{
checkNoCircularDepends
(
it
,
it
.
depends
,
mutableListOf
())
}
//load
fun
loadPlugin
(
description
:
PluginDescription
):
Boolean
{
if
(!
description
.
noCircularDepend
)
{
logger
.
error
(
"Failed to load plugin "
+
description
.
name
+
" because it has circular dependency"
)
return
false
}
//load depends first
description
.
depends
.
forEach
{
dependent
->
if
(!
pluginsFound
.
containsKey
(
dependent
))
{
logger
.
error
(
"Failed to load plugin "
+
description
.
name
+
" because it need "
+
dependent
+
" as dependency"
)
return
false
}
val
depend
=
pluginsFound
[
dependent
]
!!
//还没有加载
if
(!
depend
.
loaded
&&
!
loadPlugin
(
pluginsFound
[
dependent
]
!!
))
{
logger
.
error
(
"Failed to load plugin "
+
description
.
name
+
" because "
+
dependent
+
" as dependency failed to load"
)
return
false
}
}
//在这里所有的depends都已经加载了
//real load
logger
.
info
(
"loading plugin "
+
description
.
name
)
try
{
val
pluginClass
=
try
{
PluginClassLoader
(
(
pluginsLocation
[
description
.
name
]
!!
),
this
.
javaClass
.
classLoader
)
.
loadClass
(
description
.
basePath
)
}
catch
(
e
:
ClassNotFoundException
)
{
logger
.
info
(
"failed to find Main: "
+
description
.
basePath
+
" checking if it's kotlin's path"
)
PluginClassLoader
(
(
pluginsLocation
[
description
.
name
]
!!
),
this
.
javaClass
.
classLoader
)
.
loadClass
(
"${description.basePath}Kt"
)
}
return
try
{
val
subClass
=
pluginClass
.
asSubclass
(
PluginBase
::
class
.
java
)
val
plugin
:
PluginBase
=
subClass
.
kotlin
.
objectInstance
?:
subClass
.
getDeclaredConstructor
().
newInstance
()
description
.
loaded
=
true
logger
.
info
(
"successfully loaded plugin "
+
description
.
name
+
" version "
+
description
.
version
+
" by "
+
description
.
author
)
logger
.
info
(
description
.
info
)
nameToPluginBaseMap
[
description
.
name
]
=
plugin
pluginDescriptions
[
description
.
name
]
=
description
plugin
.
init
(
description
)
true
}
catch
(
e
:
ClassCastException
)
{
logger
.
error
(
"failed to load plugin "
+
description
.
name
+
" , Main class does not extends PluginBase "
)
false
}
}
catch
(
e
:
ClassNotFoundException
)
{
e
.
printStackTrace
()
logger
.
error
(
"failed to load plugin "
+
description
.
name
+
" , Main class not found under "
+
description
.
basePath
)
return
false
}
}
pluginsFound
.
values
.
forEach
{
loadPlugin
(
it
)
}
nameToPluginBaseMap
.
values
.
forEach
{
it
.
enable
()
}
logger
.
info
(
"""加载了${nameToPluginBaseMap.size}个插件"""
)
}
@JvmOverloads
fun
disableAllPlugins
(
throwable
:
CancellationException
?
=
null
)
{
nameToPluginBaseMap
.
values
.
forEach
{
it
.
disable
(
throwable
)
}
}
/**
* 根据插件名字找Jar的文件
* null => 没找到
*/
fun
getJarPath
(
pluginName
:
String
):
File
?
{
File
(
pluginsPath
).
listFiles
()
?.
forEach
{
file
->
if
(
file
!=
null
&&
file
.
extension
==
"jar"
)
{
val
jar
=
JarFile
(
file
)
val
pluginYml
=
jar
.
entries
().
asSequence
().
filter
{
it
.
name
.
toLowerCase
().
contains
(
"plugin.yml"
)
}.
firstOrNull
()
if
(
pluginYml
!=
null
)
{
val
description
=
PluginDescription
.
readFromContent
(
URL
(
"jar:file:"
+
file
.
absoluteFile
+
"!/"
+
pluginYml
.
name
).
openConnection
().
inputStream
.
use
{
it
.
readBytes
().
encodeToString
()
})
if
(
description
.
name
.
toLowerCase
()
==
pluginName
.
toLowerCase
())
{
return
file
}
}
}
}
return
null
}
/**
* 根据插件名字找Jar中的文件
* null => 没找到
*/
fun
getFileInJarByName
(
pluginName
:
String
,
toFind
:
String
):
InputStream
?
{
val
jarFile
=
getJarPath
(
pluginName
)
if
(
jarFile
==
null
)
{
return
null
}
val
jar
=
JarFile
(
jarFile
)
val
toFindFile
=
jar
.
entries
().
asSequence
().
filter
{
it
.
name
==
toFind
}.
firstOrNull
()
?:
return
null
return
URL
(
"jar:file:"
+
jarFile
.
absoluteFile
+
"!/"
+
toFindFile
.
name
).
openConnection
().
inputStream
}
}
mirai-console/src/main/kotlin/net/mamoe/mirai/console/pure/MiraiConsolePureLoader.kt
deleted
100644 → 0
View file @
6110cf8f
package
net.mamoe.mirai.console.pure
import
net.mamoe.mirai.console.MiraiConsole
import
kotlin.concurrent.thread
class
MiraiConsolePureLoader
{
companion
object
{
@JvmStatic
fun
main
(
args
:
Array
<
String
>)
{
MiraiConsole
.
start
(
MiraiConsoleUIPure
())
Runtime
.
getRuntime
().
addShutdownHook
(
thread
(
start
=
false
)
{
MiraiConsole
.
stop
()
})
}
}
}
\ No newline at end of file
mirai-console/src/main/kotlin/net/mamoe/mirai/console/pure/MiraiConsoleUIPure.kt
deleted
100644 → 0
View file @
6110cf8f
/*
* 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
*/
package
net.mamoe.mirai.console.pure
import
kotlinx.coroutines.delay
import
net.mamoe.mirai.Bot
import
net.mamoe.mirai.console.MiraiConsole
import
net.mamoe.mirai.console.utils.MiraiConsoleUI
import
net.mamoe.mirai.utils.DefaultLoginSolver
import
net.mamoe.mirai.utils.LoginSolver
import
net.mamoe.mirai.utils.LoginSolverInputReader
import
kotlin.concurrent.thread
class
MiraiConsoleUIPure
:
MiraiConsoleUI
{
var
requesting
=
false
var
requestStr
=
""
init
{
thread
{
while
(
true
)
{
val
input
=
readLine
()
?:
return
@
thread
if
(
requesting
)
{
requestStr
=
input
requesting
=
false
}
else
{
MiraiConsole
.
CommandProcessor
.
runConsoleCommandBlocking
(
input
)
}
}
}
}
override
fun
pushLog
(
identity
:
Long
,
message
:
String
)
{
println
(
message
)
}
override
fun
prePushBot
(
identity
:
Long
)
{
}
override
fun
pushBot
(
bot
:
Bot
)
{
}
override
fun
pushVersion
(
consoleVersion
:
String
,
consoleBuild
:
String
,
coreVersion
:
String
)
{
}
override
suspend
fun
requestInput
(
question
:
String
):
String
{
requesting
=
true
while
(
true
)
{
delay
(
50
)
if
(!
requesting
)
{
return
requestStr
}
}
}
override
fun
pushBotAdminStatus
(
identity
:
Long
,
admins
:
List
<
Long
>)
{
}
override
fun
createLoginSolver
():
LoginSolver
{
return
DefaultLoginSolver
(
reader
=
object
:
LoginSolverInputReader
{
override
suspend
fun
read
(
question
:
String
):
String
?
{
return
requestInput
(
question
)
}
}
)
}
}
mirai-console/src/main/kotlin/net/mamoe/mirai/console/utils/BotHelper.kt
deleted
100644 → 0
View file @
6110cf8f
package
net.mamoe.mirai.console.utils
import
net.mamoe.mirai.Bot
import
net.mamoe.mirai.console.MiraiConsole
import
net.mamoe.mirai.console.MiraiProperties
import
net.mamoe.mirai.console.utils.BotManagers.BOT_MANAGERS
import
net.mamoe.mirai.console.plugins.ConfigSection
import
net.mamoe.mirai.console.plugins.ConfigSectionImpl
import
net.mamoe.mirai.console.plugins.loadAsConfig
import
net.mamoe.mirai.console.plugins.withDefaultWriteSave
import
java.io.File
object
BotManagers
{
val
config
=
File
(
"${MiraiConsole.path}/bot.yml"
).
loadAsConfig
()
val
BOT_MANAGERS
:
ConfigSection
by
config
.
withDefaultWriteSave
{
ConfigSectionImpl
()
}
}
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
)
{
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
())
}
fun
Bot
.
checkManager
(
long
:
Long
):
Boolean
{
return
this
.
getManagers
().
contains
(
long
)
}
mirai-console/src/main/kotlin/net/mamoe/mirai/console/utils/MiraiConsoleUI.kt
deleted
100644 → 0
View file @
6110cf8f
/*
* 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
*/
package
net.mamoe.mirai.console.utils
import
net.mamoe.mirai.Bot
import
net.mamoe.mirai.utils.LoginSolver
/**
* 只需要实现一个这个 传入MiraiConsole 就可以绑定UI层与Console层
* 注意线程
*/
interface
MiraiConsoleUI
{
/**
* 让UI层展示一条log
*
* identity:log所属的screen, Main=0; Bot=Bot.uin
*/
fun
pushLog
(
identity
:
Long
,
message
:
String
)
/**
* 让UI层准备接受新增的一个BOT
*/
fun
prePushBot
(
identity
:
Long
)
/**
* 让UI层接受一个新的bot
* */
fun
pushBot
(
bot
:
Bot
)
fun
pushVersion
(
consoleVersion
:
String
,
consoleBuild
:
String
,
coreVersion
:
String
)
/**
* 让UI层提供一个Input
* 这个Input 不 等于 Command
*
*/
suspend
fun
requestInput
(
question
:
String
):
String
/**
* 让UI层更新BOT管理员的数据
*/
fun
pushBotAdminStatus
(
identity
:
Long
,
admins
:
List
<
Long
>
)
/**
* 由UI层创建一个LoginSolver
*/
fun
createLoginSolver
():
LoginSolver
}
\ No newline at end of file
settings.gradle
View file @
c6ae8e32
...
...
@@ -42,9 +42,6 @@ include(':mirai-core')
//include(':mirai-core-timpc')
include
(
':mirai-core-qqandroid'
)
include
(
':mirai-japt'
)
include
(
':mirai-console'
)
include
(
':mirai-console-terminal'
)
//include(':mirai-api')
include
(
':mirai-api-http'
)
include
(
':mirai-demos:mirai-demo-1'
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment