Browse Source

Update docs

Him188 6 years ago
parent
commit
490255bcd5

+ 20 - 11
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/Bot.kt

@@ -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()

+ 33 - 14
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotHelper.kt

@@ -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

+ 20 - 8
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/BotSession.kt

@@ -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
+/**
+ * 取得 [T] 的 [BotSession].
+ * 实际上是一个捷径.
+ */
+inline val <T : BotNetworkHandler<*>> T.session get() = this[ActionPacketHandler].session

+ 1 - 1
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/BotNetworkConfiguration.kt

@@ -7,7 +7,7 @@ import kotlin.jvm.JvmField
 import kotlin.jvm.JvmOverloads
 
 /**
- * 网络配置
+ * 网络和连接配置
  */
 class BotNetworkConfiguration @JvmOverloads constructor(
     /**

+ 128 - 2
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/MiraiLogger.kt

@@ -1,12 +1,39 @@
 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)

+ 2 - 8
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/DebugUtil.kt

@@ -1,13 +1,13 @@
 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()
-}

+ 14 - 10
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/MiraiLoggerJvm.kt

@@ -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
 }

+ 8 - 0
mirai-demos/mirai-demo-gentleman/build.gradle

@@ -0,0 +1,8 @@
+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
+}

+ 179 - 0
mirai-demos/mirai-demo-gentleman/src/main/kotlin/demo1/Main.kt

@@ -0,0 +1,179 @@
+@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 "停止"
+            }
+        }
+    }
+}

+ 2 - 0
settings.gradle

@@ -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')