Commit 490255bc authored by Him188's avatar Him188

Update docs

parent 28859056
......@@ -2,6 +2,7 @@
package net.mamoe.mirai
import kotlinx.coroutines.Job
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import net.mamoe.mirai.Bot.ContactSystem
......@@ -10,6 +11,7 @@ import net.mamoe.mirai.network.BotNetworkHandler
import net.mamoe.mirai.network.protocol.tim.TIMBotNetworkHandler
import net.mamoe.mirai.network.protocol.tim.packet.login.LoginResult
import net.mamoe.mirai.utils.BotNetworkConfiguration
import net.mamoe.mirai.utils.DefaultLogger
import net.mamoe.mirai.utils.MiraiLogger
import net.mamoe.mirai.utils.log
import kotlin.jvm.JvmOverloads
......@@ -28,8 +30,10 @@ data class BotAccount(
* [网络处理器][TIMBotNetworkHandler]: 可通过 [Bot.network] 访问
* [机器人账号信息][BotAccount]: 可通过 [Bot.qqAccount] 访问
*
* 若你需要得到机器人的 QQ 账号, 请访问 [Bot.qqAccount]
* 若你需要得到服务器上所有机器人列表, 请访问 [Bot.instances]
* 若需要得到机器人的 QQ 账号, 请访问 [Bot.qqAccount]
* 若需要得到服务器上所有机器人列表, 请访问 [Bot.instances]
*
* 在 BotHelper.kt 中有一些访问的捷径. 如 [Bot.getGroup]
*
*
*
......@@ -44,18 +48,19 @@ data class BotAccount(
*
*
* @author Him188moe
* @author NatrualHG
* @author NaturalHG
* @see net.mamoe.mirai.contact.Contact
*/
class Bot(val account: BotAccount, val logger: MiraiLogger) {
constructor(id: UInt, password: String) : this(BotAccount(id, password))
constructor(account: BotAccount) : this(account, DefaultLogger("Bot(" + account.id + ")"))
val contacts = ContactSystem()
var network: BotNetworkHandler<*> = TIMBotNetworkHandler(this)
init {
instances.add(this)
this.logger.identity = "Bot(" + this.account.id + ")"
}
override fun toString(): String = "Bot{qq=${account.id}}"
......@@ -85,9 +90,14 @@ class Bot(val account: BotAccount, val logger: MiraiLogger) {
* @see Bot.contacts
*/
inner class ContactSystem internal constructor() {
private val _groups = ContactList<Group>()
private lateinit var groupsUpdater: Job
val groups = ContactList<Group>()
private val groupsLock = Mutex()
val qqs = ContactList<QQ>()
private val _qqs = ContactList<QQ>() //todo 实现群列表和好友列表获取
private lateinit var qqUpdaterJob: Job
val qqs: ContactList<QQ> = _qqs
private val qqsLock = Mutex()
/**
......@@ -121,12 +131,11 @@ class Bot(val account: BotAccount, val logger: MiraiLogger) {
}
}
suspend fun UInt.qq(): QQ = getQQ(this)
suspend fun Long.qq(): QQ = getQQ(this)
suspend inline fun UInt.qq(): QQ = getQQ(this)
suspend fun UInt.group(): Group = getGroup(GroupId(this))
suspend fun GroupId.group(): Group = getGroup(this)
suspend fun GroupInternalId.group(): Group = getGroup(this)
suspend inline fun UInt.group(): Group = getGroup(GroupId(this))
suspend inline fun GroupId.group(): Group = getGroup(this)
suspend inline fun GroupInternalId.group(): Group = getGroup(this)
suspend fun close() {
this.network.close()
......
......@@ -3,6 +3,7 @@
package net.mamoe.mirai
import net.mamoe.mirai.contact.*
import net.mamoe.mirai.network.BotNetworkHandler
import net.mamoe.mirai.network.BotSession
import net.mamoe.mirai.network.protocol.tim.packet.OutgoingPacket
import net.mamoe.mirai.network.protocol.tim.packet.login.LoginResult
......@@ -10,29 +11,47 @@ import net.mamoe.mirai.network.session
import net.mamoe.mirai.utils.BotNetworkConfiguration
/*
* The mirror of functions in inner classes of [Bot]
* 在 [Bot] 中的方法的捷径
*/
//Contacts
suspend fun Bot.getQQ(number: Long): QQ = this.contacts.getQQ(number.toUInt())
suspend inline fun Bot.getQQ(number: UInt): QQ = this.contacts.getQQ(number)
suspend fun Bot.getQQ(number: UInt): QQ = this.contacts.getQQ(number)
suspend inline fun Bot.getGroup(id: GroupId): Group = this.contacts.getGroup(id)
suspend inline fun Bot.getGroup(internalId: GroupInternalId): Group = this.contacts.getGroup(internalId)
suspend fun Bot.getGroup(id: GroupId): Group = this.contacts.getGroup(id)
suspend fun Bot.getGroup(internalId: GroupInternalId): Group = this.contacts.getGroup(internalId)
val Bot.groups: ContactList<Group> get() = this.contacts.groups
val Bot.qqs: ContactList<QQ> get() = this.contacts.qqs
/**
* 取得机器人的群成员列表. 当机器人登录后成员列表就会
*/
@Suppress("WRONG_MODIFIER_TARGET")
suspend inline val Bot.groups: ContactList<Group>
get() = this.contacts.groups
inline fun <T> Bot.withSession(block: BotSession.() -> T): T = with(this.network.session) { block() }
@Suppress("WRONG_MODIFIER_TARGET")
suspend inline val Bot.qqs: ContactList<QQ>
get() = this.contacts.qqs
/**
* 以 [BotSession] 作为接收器 (receiver) 并调用 [block], 返回 [block] 的返回值.
* 这个方法将能帮助使用在 [BotSession] 中定义的一些扩展方法, 如 [BotSession.sendAndExpect]
*/
inline fun <R> Bot.withSession(block: BotSession.() -> R): R = with(this.network.session) { block() }
//NetworkHandler
suspend fun Bot.sendPacket(packet: OutgoingPacket) = this.network.sendPacket(packet)
/**
* 发送数据包
* @throws IllegalStateException 当 [BotNetworkHandler.socket] 未开启时
*/
suspend inline fun Bot.sendPacket(packet: OutgoingPacket) = this.network.sendPacket(packet)
suspend fun Bot.login(configuration: BotNetworkConfiguration.() -> Unit): LoginResult = this.network.login(BotNetworkConfiguration().apply(configuration))
/**
* 使用在默认配置基础上修改的配置登录
*/
suspend inline fun Bot.login(noinline configuration: BotNetworkConfiguration.() -> Unit): LoginResult = this.network.login(BotNetworkConfiguration().apply(configuration))
suspend fun Bot.login(): LoginResult = this.network.login(BotNetworkConfiguration.Default)
/**
* 使用默认的配置 ([BotNetworkConfiguration.Default]) 登录
*/
suspend inline fun Bot.login(): LoginResult = this.network.login(BotNetworkConfiguration.Default)
//BotAccount
val Bot.qqAccount: UInt get() = this.account.id
inline val Bot.qqAccount: UInt get() = this.account.id
\ No newline at end of file
......@@ -15,7 +15,11 @@ import net.mamoe.mirai.network.protocol.tim.packet.ServerPacket
import net.mamoe.mirai.utils.getGTK
import kotlin.coroutines.coroutineContext
internal fun TIMBotNetworkHandler.BotSession(
/**
* 构造 [BotSession] 的捷径
*/
@Suppress("FunctionName", "NOTHING_TO_INLINE")
internal inline fun TIMBotNetworkHandler.BotSession(
bot: Bot,
sessionKey: ByteArray,
socket: DataPacketSocketAdapter
......@@ -56,9 +60,8 @@ class BotSession(
private set
/**
* 发送一个数据包, 并期待接受一个特定的 [ServerPacket].
* 发送成功后, 该方法会等待收到 [ServerPacket] 直到超时.
* 由于包名可能过长, 可使用 `DataPacketSocketAdapter.sendAndExpect(PacketProcessor)` 替代.
* 发送一个数据包, 并期待接受一个特定的 [ServerPacket][P].
* 发送成功后, 该方法会等待收到 [ServerPacket][P] 直到超时.
*
* 实现方法:
* ```kotlin
......@@ -71,6 +74,8 @@ class BotSession(
*
* @param P 期待的包
* @param handler 处理期待的包
*
* @see Bot.withSession 转换接收器 (receiver, 即 `this` 的指向) 为 [BotSession]
*/
suspend inline fun <reified P : ServerPacket, R> OutgoingPacket.sendAndExpect(noinline handler: suspend (P) -> R): CompletableDeferred<R> {
val deferred: CompletableDeferred<R> =
......@@ -82,6 +87,9 @@ class BotSession(
return deferred
}
/**
* 发送一个数据包, 并期待接受一个特定的 [ServerPacket][P].
*/
suspend inline fun <reified P : ServerPacket> OutgoingPacket.sendAndExpect(): CompletableDeferred<Unit> =
sendAndExpect<P, Unit> {}
......@@ -89,8 +97,12 @@ class BotSession(
}
suspend fun BotSession.distributePacket(packet: ServerPacket) = this.socket.distributePacket(packet)
val BotSession.isOpen: Boolean get() = socket.isOpen
val BotSession.qqAccount: UInt get() = bot.account.id
suspend inline fun BotSession.distributePacket(packet: ServerPacket) = this.socket.distributePacket(packet)
inline val BotSession.isOpen: Boolean get() = socket.isOpen
inline val BotSession.qqAccount: UInt get() = bot.account.id
val <T : BotNetworkHandler<*>> T.session get() = this[ActionPacketHandler].session
\ No newline at end of file
/**
* 取得 [T] 的 [BotSession].
* 实际上是一个捷径.
*/
inline val <T : BotNetworkHandler<*>> T.session get() = this[ActionPacketHandler].session
\ No newline at end of file
......@@ -7,7 +7,7 @@ import kotlin.jvm.JvmField
import kotlin.jvm.JvmOverloads
/**
* 网络配置
* 网络和连接配置
*/
class BotNetworkConfiguration @JvmOverloads constructor(
/**
......
package net.mamoe.mirai.utils
import net.mamoe.mirai.Bot
import kotlin.jvm.JvmOverloads
/**
* 日志记录器. 所有的输出均依赖于它.
* 不同的对象可能拥有只属于自己的 logger. 通过 [identity] 来区分.
*
* 注意: 请不要直接实现这个接口, 请继承 [MiraiLoggerPlatformBase]
*
* @see MiraiLoggerPlatformBase 平台通用基础实现
*/
interface MiraiLogger {
companion object : MiraiLogger by PlatformLogger("[TOP Level]")
/**
* 顶层日志记录器
*/
companion object : MiraiLogger by DefaultLogger("TOP Level")
var identity: String?
/**
* 随从. 在 this 中调用所有方法后都应继续往 [follower] 传递调用.
* [follower] 的存在可以让一次日志被多个日志记录器记录.
*
* 例:
* ```kotlin
* val bot = Bot( ... )
* bot.follower = MyOwnLogger()
*
* bot.logInfo("Hi")
* ```
* 在这个例子中的 `MyOwnLogger` 将可以记录到 "Hi".
*/
var follower: MiraiLogger?
fun logInfo(any: Any?) = log(any)
fun log(e: Throwable)
......@@ -24,8 +51,107 @@ interface MiraiLogger {
fun logGreen(any: Any?)
fun logBlue(any: Any?)
/**
* 添加一个 [follower], 返回 [follower]
* 它只会把 `this` 的属性 [MiraiLogger.follower] 修改为这个函数的参数 [follower], 然后返回这个参数.
* 若 [MiraiLogger.follower] 已经有值, 则会替换掉这个值.
*
* @see follower
*/
operator fun plus(follower: MiraiLogger): MiraiLogger
/**
* 添加一个 [follower]
* 若 [MiraiLogger.follower] 已经有值, 则会对这个值调用 [plusAssign]. 即会在日志记录器链的末尾添加这个参数 [follower]
*
* @see follower
*/
operator fun plusAssign(follower: MiraiLogger)
}
/**
* 平台基类.
* 实现了 [follower] 的调用传递
*/
abstract class MiraiLoggerPlatformBase : MiraiLogger {
final override var follower: MiraiLogger? = null
final override fun logInfo(any: Any?) = log(any)
final override fun log(e: Throwable) {
log0(e)
follower?.log(e)
}
final override fun log(any: Any?) {
log0(any)
follower?.log(any)
}
final override fun logError(any: Any?) {
logError0(any)
follower?.logError(any)
}
final override fun logDebug(any: Any?) {
logDebug0(any)
follower?.logDebug(any)
}
final override fun logCyan(any: Any?) {
logCyan0(any)
follower?.logCyan(any)
}
final override fun logPurple(any: Any?) {
logPurple0(any)
follower?.logPurple(any)
}
final override fun logGreen(any: Any?) {
logGreen0(any)
follower?.logGreen(any)
}
final override fun logBlue(any: Any?) {
logBlue0(any)
follower?.logBlue(any)
}
protected abstract fun log0(e: Throwable)
protected abstract fun log0(any: Any?)
protected abstract fun logError0(any: Any?)
protected abstract fun logDebug0(any: Any?)
protected abstract fun logCyan0(any: Any?)
protected abstract fun logPurple0(any: Any?)
protected abstract fun logGreen0(any: Any?)
protected abstract fun logBlue0(any: Any?)
override fun plus(follower: MiraiLogger): MiraiLogger {
this.follower = follower
return follower
}
override fun plusAssign(follower: MiraiLogger) =
if (this.follower == null) this.follower = follower
else this.follower!! += follower
}
expect class PlatformLogger @JvmOverloads constructor(identity: String? = null) : MiraiLogger
/**
* 用于创建默认的日志记录器. 在一些需要使用日志的 Mirai 的组件, 如 [Bot], 都会通过这个函数构造日志记录器
*/
var DefaultLogger: (identity: String?) -> PlatformLogger = { PlatformLogger() }
/**
* 当前平台的默认的日志记录器.
* 在 _JVM 控制台_ 端的实现为 [println]
*
* 不应该直接构造这个类的实例. 需使用 [DefaultLogger]
*/
expect class PlatformLogger @JvmOverloads internal constructor(identity: String? = null) : MiraiLoggerPlatformBase
/**
* 在顶层日志记录这个异常
*/
fun Throwable.log() = MiraiLogger.log(this)
\ No newline at end of file
package net.mamoe.mirai.utils.io
import kotlinx.io.core.*
import net.mamoe.mirai.utils.DefaultLogger
import net.mamoe.mirai.utils.MiraiLogger
import net.mamoe.mirai.utils.PlatformLogger
import net.mamoe.mirai.utils.hexToBytes
import net.mamoe.mirai.utils.toIoBuffer
internal object DebugLogger : MiraiLogger by PlatformLogger("Packet Debug")
internal object DebugLogger : MiraiLogger by DefaultLogger("Packet Debug")
internal fun debugPrintln(any: Any?) = DebugLogger.logPurple(any)
......@@ -80,9 +80,3 @@ internal fun ByteArray.printColorizedHex(name: String = "", ignoreUntilFirstCons
expect fun printCompareHex(hex1s: String, hex2s: String): String
expect fun String.printColorize(ignoreUntilFirstConst: Boolean = false): String
fun main() {
"00 02 3E 03 3F A2 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 39 00 00 00 0B 00 00 00 2E 51 51 E7 A9 BA E9 97 B4 20 0A 20 20 E6 9C 89 E6 96 B0 E8 AE BF E5 AE A2 20 0A 20 20 E6 9C 89 E6 96 B0 E5 A5 BD E5 8F 8B E5 8A A8 E6 80 81 00 00 01 2C 00 00 00 00"
.printStringFromHex()
}
\ No newline at end of file
......@@ -5,17 +5,20 @@ import java.util.*
actual typealias PlatformLogger = Console
open class Console @JvmOverloads constructor(
/**
* JVM 控制台日志实现
*/
open class Console @JvmOverloads internal constructor(
override var identity: String? = null
) : MiraiLogger {
override fun logGreen(any: Any?) = println(any.toString(), LoggerTextFormat.GREEN)
override fun logPurple(any: Any?) = println(any.toString(), LoggerTextFormat.LIGHT_PURPLE)
override fun logBlue(any: Any?) = println(any.toString(), LoggerTextFormat.BLUE)
override fun logCyan(any: Any?) = println(any.toString(), LoggerTextFormat.LIGHT_CYAN)
override fun logError(any: Any?) = println(any.toString(), LoggerTextFormat.RED)
override fun log(e: Throwable) = e.printStackTrace()
override fun log(any: Any?) = println(any.toString())//kotlin println
override fun logDebug(any: Any?) {
) : MiraiLoggerPlatformBase() {
override fun logGreen0(any: Any?) = println(any.toString(), LoggerTextFormat.GREEN)
override fun logPurple0(any: Any?) = println(any.toString(), LoggerTextFormat.LIGHT_PURPLE)
override fun logBlue0(any: Any?) = println(any.toString(), LoggerTextFormat.BLUE)
override fun logCyan0(any: Any?) = println(any.toString(), LoggerTextFormat.LIGHT_CYAN)
override fun logError0(any: Any?) = println(any.toString(), LoggerTextFormat.RED)
override fun log0(e: Throwable) = e.printStackTrace()
override fun log0(any: Any?) = println(any.toString())//kotlin println
override fun logDebug0(any: Any?) {
if (DEBUGGING) {
println(any.toString(), LoggerTextFormat.YELLOW)
}
......@@ -33,6 +36,7 @@ open class Console @JvmOverloads constructor(
}
private val DEBUGGING: Boolean by lazy {
//todo 添加环境变量检测
//avoid inspections
true
}
\ No newline at end of file
apply plugin: "kotlin"
apply plugin: "java"
dependencies {
compile project(":mirai-core")
api group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib-jdk8', version: kotlin_version
api group: 'org.jetbrains.kotlinx', name: 'kotlinx-coroutines-core', version: coroutines_version
}
@file:Suppress("EXPERIMENTAL_UNSIGNED_LITERALS", "EXPERIMENTAL_API_USAGE")
package demo1
import kotlinx.coroutines.delay
import kotlinx.coroutines.withTimeoutOrNull
import net.mamoe.mirai.Bot
import net.mamoe.mirai.BotAccount
import net.mamoe.mirai.event.events.FriendMessageEvent
import net.mamoe.mirai.event.events.GroupMessageEvent
import net.mamoe.mirai.event.subscribeAll
import net.mamoe.mirai.event.subscribeAlways
import net.mamoe.mirai.event.subscribeUntilFalse
import net.mamoe.mirai.login
import net.mamoe.mirai.message.Image
import net.mamoe.mirai.message.ImageId
import net.mamoe.mirai.message.PlainText
import net.mamoe.mirai.message.firstOrNull
import net.mamoe.mirai.network.protocol.tim.packet.OutgoingRawPacket
import net.mamoe.mirai.network.protocol.tim.packet.action.uploadImage
import net.mamoe.mirai.network.protocol.tim.packet.login.LoginResult
import net.mamoe.mirai.network.session
import net.mamoe.mirai.qqAccount
import net.mamoe.mirai.utils.MiraiLogger
import net.mamoe.mirai.utils.hexToBytes
import net.mamoe.mirai.utils.io.toUHexString
import net.mamoe.mirai.utils.toByteArray
import net.mamoe.mirai.utils.toExternalImage
import java.io.File
private fun readTestAccount(): BotAccount? {
val file = File("testAccount.txt")
if (!file.exists() || !file.canRead()) {
return null
}
val lines = file.readLines()
return try {
BotAccount(lines[0].toUInt(), lines[1])
} catch (e: IndexOutOfBoundsException) {
null
}
}
@Suppress("UNUSED_VARIABLE")
suspend fun main() {
val bot = Bot(
readTestAccount() ?: BotAccount(//填写你的账号
id = 1994701121u,
password = "123456"
)
)
bot.login {
randomDeviceName = false
}.let {
if (it != LoginResult.SUCCESS) {
MiraiLogger.logError("Login failed: " + it.name)
bot.close()
}
}
subscribeAlways<GroupMessageEvent> {
if (it.message eq "复读" && it.group.internalId.value == 580266363u) {
it.reply(it.message)
}
}
// 使用 Bot 的扩展方法监听, 将在处理事件时得到一个 this: Bot.
// 这样可以很方便地调用 Bot 内的一些扩展方法如 UInt.qq():QQ
bot.subscribeAlways<FriendMessageEvent> {
// this: Bot
// it: FriendMessageEvent
// 获取第一个纯文本消息, 获取不到会抛出 NoSuchElementException
// val firstText = it.message.first<PlainText>()
val firstText = it.message.firstOrNull<PlainText>()
// 获取第一个图片
val firstImage = it.message.firstOrNull<Image>()
when {
it.message eq "你好" -> it.reply("你好!")
"复读" in it.message -> it.sender.sendMessage(it.message)
"发群消息" in it.message -> 580266363u.group().sendMessage(it.message.toString().substringAfter("发群消息"))
"直接发送包" in it.message -> {
val d =
("01 " + 1994701021u.toByteArray().toUHexString() + " 3E 03 3F A2 00 00 02 BB 00 0A 00 01 00 01 00 5E 4F 53 52 6F 6F 74 3A 43 3A 5C 55 73 65 72 73 5C 48 69 6D 31 38 5C 44 6F 63 75 6D 65 6E 74 73 5C 54 65 6E 63 65 6E 74 20 46 69 6C 65 73 5C 31 30 34 30 34 30 30 32 39 30 5C 49 6D 61 67 65 5C 43 32 43 5C 7B 47 47 42 7E 49 31 5A 4D 43 28 25 49 4D 5A 5F 47 55 51 36 35 5D 51 2E 6A 70 67 00 00 04 7D 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 35 02")
.hexToBytes()
it.bot.network.socket.sendPacket(
OutgoingRawPacket(
0x01_BDu,
it.bot.qqAccount,
"00 00 00 01 2E 01 00 00 69 35".hexToBytes(),
it.bot.network.session.sessionKey,
d
)
)
}
"上传好友图片" in it.message -> withTimeoutOrNull(5000) {
val filename = it.message.toString().substringAfter("上传好友图片")
val id = 1040400290u.qq()
.uploadImage(File("C:\\Users\\Him18\\Desktop\\$filename").toExternalImage())
it.reply(id.value)
delay(100)
it.reply(Image(id))
}
"上传群图片" in it.message -> withTimeoutOrNull(5000) {
val filename = it.message.toString().substringAfter("上传群图片")
val image = File(
"C:\\Users\\Him18\\Desktop\\$filename"
).toExternalImage()
920503456u.group().uploadImage(image)
it.reply(image.groupImageId.value)
delay(100)
920503456u.group().sendMessage(Image(image.groupImageId))
}
"发群图片" in it.message -> {
920503456u.group().sendMessage(Image(ImageId(it.message.toString().substringAfter("发群图片"))))
}
"发好友图片" in it.message -> {
it.reply(Image(ImageId(it.message.toString().substringAfter("发好友图片"))))
}
/*it.event eq "发图片群" -> sendGroupMessage(Group(session.bot, 580266363), PlainText("test") + UnsolvedImage(File("C:\\Users\\Him18\\Desktop\\faceImage_1559564477775.jpg")).also { image ->
image.upload(session, Group(session.bot, 580266363)).of()
})*/
it.message eq "发图片群2" -> 580266363u.group().sendMessage(Image(ImageId("{7AA4B3AA-8C3C-0F45-2D9B-7F302A0ACEAA}.jpg")))
/* it.event eq "发图片" -> sendFriendMessage(it.sender, PlainText("test") + UnsolvedImage(File("C:\\Users\\Him18\\Desktop\\faceImage_1559564477775.jpg")).also { image ->
image.upload(session, it.sender).of()
})*/
it.message eq "发图片2" -> it.reply(PlainText("test") + Image(ImageId("{7AA4B3AA-8C3C-0F45-2D9B-7F302A0ACEAA}.jpg")))
else -> {
}
}
}
//DSL 监听
subscribeAll<FriendMessageEvent> {
always {
//获取第一个纯文本消息
val firstText = it.message.firstOrNull<PlainText>()
}
}
demo2()
bot.network.awaitDisconnection()//等到直到断开连接
}
/**
* 实现功能:
* 对机器人说 "记笔记", 机器人记录之后的所有消息.
* 对机器人说 "停止", 机器人停止
*/
suspend fun demo2() {
subscribeAlways<FriendMessageEvent> { event ->
if (event.message eq "记笔记") {
subscribeUntilFalse<FriendMessageEvent> {
it.reply("你发送了 ${it.message}")
it.message eq "停止"
}
}
}
}
\ No newline at end of file
......@@ -3,8 +3,10 @@ include(':mirai-core')
include(':mirai-console')
include(':mirai-api')
include(':mirai-demos:mirai-demo-1')
include(':mirai-demos:mirai-demo-gentleman')
include(':mirai-demos')
include(':mirai-debug')
project(':mirai-demos:mirai-demo-1').projectDir = file('mirai-demos/mirai-demo-1')
project(':mirai-demos:mirai-demo-gentleman').projectDir = file('mirai-demos/mirai-demo-gentleman')
enableFeaturePreview('GRADLE_METADATA')
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment