mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-20 21:15:18 +08:00
Merge remote-tracking branch 'origin/master'
# Conflicts: # mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsole.kt
This commit is contained in:
commit
49673262ad
@ -2,6 +2,14 @@
|
||||
|
||||
开发版本. 频繁更新, 不保证高稳定性
|
||||
|
||||
## `0.15.2` 2020/2/18
|
||||
|
||||
### mirai-core
|
||||
- 尝试修复 `atomicfu` 编译错误的问题
|
||||
|
||||
### mirai-core-qqandroid
|
||||
- 查询群信息失败后重试
|
||||
|
||||
## `0.15.1` 2020/2/15
|
||||
|
||||
### mirai-core
|
||||
|
20
README.md
20
README.md
@ -57,6 +57,10 @@ TIM PC (2.3.2 版本,2019 年 8 月)协议的实现,相较于 core,仅
|
||||
## Use as a library
|
||||
**mirai-core 为独立设计, 可以作为库内置于任意 Java(JVM)/Android 项目中使用.**
|
||||
|
||||
请将 `VERSION` 替换为最新的版本(如 `0.15.0`):
|
||||
[![Download](https://api.bintray.com/packages/him188moe/mirai/mirai-core/images/download.svg)](https://bintray.com/him188moe/mirai/mirai-core/)
|
||||
**Mirai 目前还处于实验性阶段, 我们无法保证任何稳定性, API 也可能会随时修改.**
|
||||
|
||||
### Maven
|
||||
Kotlin 在 Maven 上只支持 JVM 平台.
|
||||
```xml
|
||||
@ -71,7 +75,7 @@ Kotlin 在 Maven 上只支持 JVM 平台.
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>net.mamoe</groupId>
|
||||
<artifactId>mirai-core-qqandroid-jvm</artifactId>
|
||||
<artifactId>mirai-core-qqandroid</artifactId>
|
||||
<version>0.15.1</version> <!-- 替换版本为最新版本 -->
|
||||
</dependency>
|
||||
</dependencies>
|
||||
@ -85,25 +89,21 @@ repositories{
|
||||
}
|
||||
```
|
||||
若您需要使用在跨平台项目, 则要对各个目标平台添加不同的依赖,这与 kotlin 相关多平台库的依赖是类似的。
|
||||
**若您只需要使用在单一平台, 则只需要添加一项该平台的依赖. 如只在 JVM 运行则只需要`-jvm`的依赖**
|
||||
|
||||
请将 `VERSION` 替换为最新的版本(如 `0.15.0`):
|
||||
[![Download](https://api.bintray.com/packages/him188moe/mirai/mirai-core/images/download.svg)](https://bintray.com/him188moe/mirai/mirai-core/)
|
||||
**Mirai 目前还处于实验性阶段, 我们无法保证任何稳定性, API 也可能会随时修改.**
|
||||
**若您只需要使用在单一平台, 则只需要添加一项该平台的依赖.**
|
||||
|
||||
**注意:**
|
||||
Mirai 核心由 API 模块(`mirai-core`)和协议模块组成。
|
||||
只添加 API 模块将无法正常工作。
|
||||
现在只推荐使用 QQAndroid 协议,请参照下文选择对应目标平台的依赖添加。
|
||||
|
||||
**jvm** (JVM 平台)
|
||||
```kotlin
|
||||
implementation("net.mamoe:mirai-core-qqandroid:VERSION")
|
||||
```
|
||||
**common** (通用平台)
|
||||
```kotlin
|
||||
implementation("net.mamoe:mirai-core-qqandroid-common:VERSION")
|
||||
```
|
||||
**jvm** (JVM 平台)
|
||||
```kotlin
|
||||
implementation("net.mamoe:mirai-core-qqandroid-jvm:VERSION")
|
||||
```
|
||||
**android** (Android 平台)
|
||||
```kotlin
|
||||
implementation("net.mamoe:mirai-core-qqandroid-android:VERSION")
|
||||
|
@ -1,7 +1,7 @@
|
||||
# style guide
|
||||
kotlin.code.style=official
|
||||
# config
|
||||
mirai_version=0.15.1
|
||||
mirai_version=0.15.2
|
||||
mirai_japt_version=1.0.1
|
||||
kotlin.incremental.multiplatform=true
|
||||
kotlin.parallel.tasks.in.project=true
|
||||
|
@ -1,4 +1,5 @@
|
||||
// 部分源码来自 kotlinx.coroutines
|
||||
// Source code from kotlinx.coroutines
|
||||
|
||||
def pomConfig = {
|
||||
licenses {
|
||||
@ -12,6 +13,7 @@ def pomConfig = {
|
||||
developer {
|
||||
id "mamoe"
|
||||
name "Mamoe Technologies"
|
||||
email "support@mamoe.net"
|
||||
}
|
||||
}
|
||||
scm {
|
||||
|
@ -6,7 +6,7 @@ plugins {
|
||||
}
|
||||
|
||||
javafx {
|
||||
version = "11"
|
||||
version = "13.0.2"
|
||||
modules = listOf("javafx.controls")
|
||||
//mainClassName = "Application"
|
||||
}
|
||||
|
@ -24,8 +24,8 @@ class MiraiGraphicalUIController : Controller(), MiraiConsoleUI {
|
||||
val botList = observableListOf<BotModel>()
|
||||
val consoleInfo = ConsoleInfo()
|
||||
|
||||
fun login(qq: String, psd: String) {
|
||||
MiraiConsole.CommandListener.commandChannel.offer("/login $qq $psd")
|
||||
suspend fun login(qq: String, psd: String) {
|
||||
MiraiConsole.CommandListener.commandChannel.send("/login $qq $psd")
|
||||
}
|
||||
|
||||
override fun pushLog(identity: Long, message: String) = Platform.runLater {
|
||||
|
@ -1,7 +1,7 @@
|
||||
package net.mamoe.mirai.console.graphical.view
|
||||
|
||||
import com.jfoenix.controls.JFXTextField
|
||||
import javafx.beans.property.SimpleStringProperty
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import net.mamoe.mirai.console.graphical.controller.MiraiGraphicalUIController
|
||||
import net.mamoe.mirai.console.graphical.util.jfxButton
|
||||
import net.mamoe.mirai.console.graphical.util.jfxPasswordfield
|
||||
@ -24,7 +24,9 @@ class LoginFragment : Fragment() {
|
||||
}
|
||||
}
|
||||
jfxButton("登录").action {
|
||||
controller.login(qq.value, psd.value)
|
||||
runBlocking {
|
||||
controller.login(qq.value, psd.value)
|
||||
}
|
||||
close()
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,6 @@ 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.SwingTerminalFontConfiguration
|
||||
import com.googlecode.lanterna.terminal.swing.SwingTerminalFrame
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.io.close
|
||||
@ -23,7 +22,6 @@ import net.mamoe.mirai.console.MiraiConsoleTerminalUI.LoggerDrawer.redrawLogs
|
||||
import net.mamoe.mirai.utils.LoginSolver
|
||||
import net.mamoe.mirai.utils.createCharImg
|
||||
import net.mamoe.mirai.utils.writeChannel
|
||||
import java.awt.Font
|
||||
import java.io.File
|
||||
import java.io.OutputStream
|
||||
import java.io.PrintStream
|
||||
@ -126,12 +124,12 @@ object MiraiConsoleTerminalUI : MiraiConsoleUI {
|
||||
}
|
||||
|
||||
|
||||
fun provideInput(input: String) {
|
||||
suspend fun provideInput(input: String) {
|
||||
if (requesting) {
|
||||
requestResult = input
|
||||
requesting = false
|
||||
} else {
|
||||
MiraiConsole.CommandListener.commandChannel.offer(
|
||||
MiraiConsole.CommandListener.commandChannel.send(
|
||||
commandBuilder.toString()
|
||||
)
|
||||
}
|
||||
@ -336,7 +334,9 @@ object MiraiConsoleTerminalUI : MiraiConsoleUI {
|
||||
update()
|
||||
}
|
||||
KeyType.Enter -> {
|
||||
provideInput(commandBuilder.toString())
|
||||
runBlocking {
|
||||
provideInput(commandBuilder.toString())
|
||||
}
|
||||
emptyCommand()
|
||||
}
|
||||
KeyType.Escape -> {
|
||||
|
@ -6,9 +6,9 @@
|
||||
| 名字 | 介绍 |
|
||||
| --- | --- |
|
||||
| Mirai-Console-Pure | 最纯净版, CLI环境, 通过标准输入与标准输出 交互 |
|
||||
| Mirai-Console-Terminal | (UNIX)Terminal环境 提供简介好用的富文本控制台 |
|
||||
| Mirai-Console-Terminal | (UNIX)Terminal环境 提供简洁的富文本控制台 |
|
||||
| Mirai-Console-Android | 安卓APP (TODO) |
|
||||
| Mirai-Console-Graphical | JavaFX的图形化界面, 有Native版本(.jar/.exe/.dmg) |
|
||||
| Mirai-Console-Graphical | JavaFX的图形化界面 (.jar/.exe/.dmg) |
|
||||
| Mirai-Console-WebPanel | Web Panel操作(TODO) |
|
||||
| Mirai-Console-Ios | IOS APP (TODO) |
|
||||
|
||||
|
@ -42,7 +42,7 @@ object CommandManager {
|
||||
registeredCommand.remove(commandName)
|
||||
}
|
||||
|
||||
fun runCommand(fullCommand: String): Boolean {
|
||||
suspend fun runCommand(fullCommand: String): Boolean {
|
||||
val blocks = fullCommand.split(" ")
|
||||
val commandHead = blocks[0].replace("/", "")
|
||||
if (!registeredCommand.containsKey(commandHead)) {
|
||||
@ -66,7 +66,7 @@ interface ICommand {
|
||||
val name: String
|
||||
val alias: List<String>
|
||||
val description: String
|
||||
fun onCommand(args: List<String>): Boolean
|
||||
suspend fun onCommand(args: List<String>): Boolean
|
||||
fun register()
|
||||
}
|
||||
|
||||
@ -77,9 +77,9 @@ abstract class Command(
|
||||
) : ICommand {
|
||||
/**
|
||||
* 最高优先级监听器
|
||||
* 如果return [false] 这次指令不会被[PluginBase]的全局onCommand监听器监听
|
||||
* 如果 return `false` 这次指令不会被 [PluginBase] 的全局 onCommand 监听器监听
|
||||
* */
|
||||
open override fun onCommand(args: List<String>): Boolean {
|
||||
open override suspend fun onCommand(args: List<String>): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
@ -92,9 +92,9 @@ class AnonymousCommand internal constructor(
|
||||
override val name: String,
|
||||
override val alias: List<String>,
|
||||
override val description: String,
|
||||
val onCommand: ICommand.(args: List<String>) -> Boolean
|
||||
val onCommand: suspend ICommand.(args: List<String>) -> Boolean
|
||||
) : ICommand {
|
||||
override fun onCommand(args: List<String>): Boolean {
|
||||
override suspend fun onCommand(args: List<String>): Boolean {
|
||||
return onCommand.invoke(this, args)
|
||||
}
|
||||
|
||||
@ -107,9 +107,9 @@ class CommandBuilder internal constructor() {
|
||||
var name: String? = null
|
||||
var alias: List<String>? = null
|
||||
var description: String = ""
|
||||
var onCommand: (ICommand.(args: List<String>) -> Boolean)? = null
|
||||
var onCommand: (suspend ICommand.(args: List<String>) -> Boolean)? = null
|
||||
|
||||
fun onCommand(commandProcess: ICommand.(args: List<String>) -> Boolean) {
|
||||
fun onCommand(commandProcess: suspend ICommand.(args: List<String>) -> Boolean) {
|
||||
onCommand = commandProcess
|
||||
}
|
||||
|
||||
|
@ -9,20 +9,20 @@ package net.mamoe.mirai.console
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.api.http.MiraiHttpAPIServer
|
||||
import net.mamoe.mirai.api.http.generateSessionKey
|
||||
import net.mamoe.mirai.console.MiraiConsole.CommandListener.processNextCommandLine
|
||||
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.plugins.withDefaultWriteSave
|
||||
import net.mamoe.mirai.contact.sendMessage
|
||||
import net.mamoe.mirai.utils.*
|
||||
import net.mamoe.mirai.utils.SimpleLogger
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
import java.util.concurrent.LinkedBlockingQueue
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
|
||||
object MiraiConsole {
|
||||
@ -130,37 +130,35 @@ object MiraiConsole {
|
||||
val qqPassword = it[1]
|
||||
logger("[Bot Login]", 0, "login...")
|
||||
try {
|
||||
runBlocking {
|
||||
frontEnd.prePushBot(qqNumber)
|
||||
val bot = Bot(qqNumber, qqPassword) {
|
||||
this.loginSolver = frontEnd.createLoginSolver()
|
||||
this.botLoggerSupplier = {
|
||||
SimpleLogger("BOT $qqNumber]") { _, message, e ->
|
||||
logger("[BOT $qqNumber]", qqNumber, message)
|
||||
if (e != null) {
|
||||
logger("[NETWORK ERROR]", qqNumber, e.toString())//因为在一页 所以可以不打QQ
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
this.networkLoggerSupplier = {
|
||||
SimpleLogger("BOT $qqNumber") { _, message, e ->
|
||||
logger("[NETWORK]", qqNumber, message)//因为在一页 所以可以不打QQ
|
||||
if (e != null) {
|
||||
logger("[NETWORK ERROR]", qqNumber, e.toString())//因为在一页 所以可以不打QQ
|
||||
e.printStackTrace()
|
||||
}
|
||||
frontEnd.prePushBot(qqNumber)
|
||||
val bot = Bot(qqNumber, qqPassword) {
|
||||
this.loginSolver = frontEnd.createLoginSolver()
|
||||
this.botLoggerSupplier = {
|
||||
SimpleLogger("BOT $qqNumber]") { _, message, e ->
|
||||
logger("[BOT $qqNumber]", qqNumber, message)
|
||||
if (e != null) {
|
||||
logger("[NETWORK ERROR]", qqNumber, e.toString())//因为在一页 所以可以不打QQ
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
this.networkLoggerSupplier = {
|
||||
SimpleLogger("BOT $qqNumber") { _, message, e ->
|
||||
logger("[NETWORK]", qqNumber, message)//因为在一页 所以可以不打QQ
|
||||
if (e != null) {
|
||||
logger("[NETWORK ERROR]", qqNumber, e.toString())//因为在一页 所以可以不打QQ
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
bot.login()
|
||||
logger(
|
||||
"[Bot Login]",
|
||||
0,
|
||||
"$qqNumber login successes"
|
||||
)
|
||||
frontEnd.pushBot(bot)
|
||||
}
|
||||
bot.login()
|
||||
logger(
|
||||
"[Bot Login]",
|
||||
0,
|
||||
"$qqNumber login successes"
|
||||
)
|
||||
frontEnd.pushBot(bot)
|
||||
} catch (e: Exception) {
|
||||
logger(
|
||||
"[Bot Login]",
|
||||
@ -288,20 +286,19 @@ object MiraiConsole {
|
||||
}
|
||||
}
|
||||
|
||||
object CommandListener {
|
||||
val commandChannel: Queue<String> = LinkedBlockingQueue<String>()
|
||||
fun start() {
|
||||
thread {
|
||||
processNextCommandLine()
|
||||
}
|
||||
object CommandListener : Job by {
|
||||
GlobalScope.launch(start = CoroutineStart.LAZY) {
|
||||
processNextCommandLine()
|
||||
}
|
||||
}() {
|
||||
val commandChannel: Channel<String> = Channel()
|
||||
|
||||
tailrec fun processNextCommandLine() {
|
||||
suspend fun processNextCommandLine() {
|
||||
if (allDown) {
|
||||
return
|
||||
}
|
||||
var fullCommand = commandChannel.take(1)[0]
|
||||
if (fullCommand != null) {
|
||||
for (command in commandChannel) {
|
||||
var fullCommand = command
|
||||
if (!fullCommand.startsWith("/")) {
|
||||
fullCommand = "/$fullCommand"
|
||||
}
|
||||
@ -309,7 +306,6 @@ object MiraiConsole {
|
||||
logger("未知指令 $fullCommand")
|
||||
}
|
||||
}
|
||||
processNextCommandLine();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,25 +1,28 @@
|
||||
package net.mamoe.mirai.console
|
||||
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import net.mamoe.mirai.Bot
|
||||
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 {
|
||||
class MiraiConsoleUIPure : MiraiConsoleUI {
|
||||
var requesting = false
|
||||
var requestStr = ""
|
||||
|
||||
init {
|
||||
thread {
|
||||
while (true) {
|
||||
val input = readLine() ?: ""
|
||||
val input = readLine() ?: return@thread
|
||||
if (requesting) {
|
||||
requestStr = input
|
||||
requesting = false
|
||||
} else {
|
||||
MiraiConsole.CommandListener.commandChannel.offer(input)
|
||||
runBlocking {
|
||||
MiraiConsole.CommandListener.commandChannel.send(input)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -118,5 +118,9 @@ kotlin {
|
||||
}
|
||||
}
|
||||
}
|
||||
//
|
||||
//tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
|
||||
// kotlinOptions.jvmTarget = "1.8"
|
||||
//}
|
||||
|
||||
apply(from = rootProject.file("gradle/publish.gradle"))
|
||||
|
@ -215,11 +215,14 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
|
||||
.sendAndExpect<FriendList.GetTroopListSimplify.Response>(retry = 2)
|
||||
|
||||
troopListData.groups.forEach { troopNum ->
|
||||
launch {
|
||||
try {
|
||||
// 别用 fun, 别 val, 编译失败警告
|
||||
lateinit var loadGroup: suspend () -> Unit
|
||||
|
||||
loadGroup = suspend {
|
||||
tryNTimesOrException(3) {
|
||||
bot.groups.delegate.addLast(
|
||||
@Suppress("DuplicatedCode")
|
||||
GroupImpl(
|
||||
(GroupImpl(
|
||||
bot = bot,
|
||||
coroutineContext = bot.coroutineContext,
|
||||
id = troopNum.groupCode,
|
||||
@ -241,12 +244,20 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
|
||||
this.delegate.groupCode = troopNum.groupCode
|
||||
},
|
||||
members = bot.queryGroupMemberList(troopNum.groupUin, troopNum.groupCode, troopNum.dwGroupOwnerUin)
|
||||
)
|
||||
))
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
}?.let {
|
||||
logger.error("群${troopNum.groupCode}的列表拉取失败, 一段时间后将会重试")
|
||||
logger.error(e)
|
||||
logger.error(it)
|
||||
this@QQAndroidBotNetworkHandler.launch {
|
||||
delay(10_000)
|
||||
loadGroup()
|
||||
}
|
||||
}
|
||||
Unit // 别删, 编译失败警告
|
||||
}
|
||||
launch {
|
||||
loadGroup()
|
||||
}
|
||||
}
|
||||
logger.info("群组列表与群成员加载完成, 共 ${troopListData.groups.size}个")
|
||||
|
@ -150,5 +150,9 @@ kotlin {
|
||||
}
|
||||
}
|
||||
}
|
||||
//
|
||||
//tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
|
||||
// kotlinOptions.jvmTarget = "1.8"
|
||||
//}
|
||||
|
||||
apply(from = rootProject.file("gradle/publish.gradle"))
|
||||
|
@ -0,0 +1,16 @@
|
||||
package net.mamoe.mirai.utils
|
||||
|
||||
private var isAddSuppressedSupported: Boolean = true
|
||||
|
||||
@MiraiInternalAPI
|
||||
@Suppress("EXTENSION_SHADOWED_BY_MEMBER")
|
||||
actual fun Throwable.addSuppressed(e: Throwable) {
|
||||
if (!isAddSuppressedSupported) {
|
||||
return
|
||||
}
|
||||
try {
|
||||
this.addSuppressed(e)
|
||||
} catch (e: Exception) {
|
||||
isAddSuppressedSupported = false
|
||||
}
|
||||
}
|
@ -7,6 +7,9 @@
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
@file:JvmMultifileClass
|
||||
@file:JvmName("MessageUtils")
|
||||
|
||||
@file:Suppress("EXPERIMENTAL_API_USAGE")
|
||||
|
||||
package net.mamoe.mirai.message.data
|
||||
@ -14,6 +17,8 @@ package net.mamoe.mirai.message.data
|
||||
import net.mamoe.mirai.contact.Member
|
||||
import net.mamoe.mirai.contact.groupCardOrNick
|
||||
import net.mamoe.mirai.utils.MiraiInternalAPI
|
||||
import kotlin.jvm.JvmMultifileClass
|
||||
import kotlin.jvm.JvmName
|
||||
|
||||
|
||||
/**
|
||||
|
@ -7,8 +7,14 @@
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
@file:JvmMultifileClass
|
||||
@file:JvmName("MessageUtils")
|
||||
|
||||
package net.mamoe.mirai.message.data
|
||||
|
||||
import kotlin.jvm.JvmMultifileClass
|
||||
import kotlin.jvm.JvmName
|
||||
|
||||
/**
|
||||
* "@全体成员"
|
||||
*
|
||||
|
@ -7,8 +7,13 @@
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
@file:JvmMultifileClass
|
||||
@file:JvmName("MessageUtils")
|
||||
|
||||
package net.mamoe.mirai.message.data
|
||||
|
||||
import kotlin.jvm.JvmMultifileClass
|
||||
import kotlin.jvm.JvmName
|
||||
import kotlin.jvm.JvmStatic
|
||||
|
||||
/**
|
||||
|
@ -7,6 +7,9 @@
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
@file:JvmMultifileClass
|
||||
@file:JvmName("MessageUtils")
|
||||
|
||||
@file:Suppress("EXPERIMENTAL_API_USAGE")
|
||||
|
||||
package net.mamoe.mirai.message.data
|
||||
@ -14,13 +17,18 @@ package net.mamoe.mirai.message.data
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.Transient
|
||||
import net.mamoe.mirai.utils.io.chunkedHexToBytes
|
||||
import kotlin.js.JsName
|
||||
import kotlin.jvm.JvmMultifileClass
|
||||
import kotlin.jvm.JvmName
|
||||
import kotlin.jvm.JvmStatic
|
||||
|
||||
/**
|
||||
* 自定义表情 (收藏的表情), 图片
|
||||
*/
|
||||
sealed class Image : Message {
|
||||
companion object Key : Message.Key<Image> {
|
||||
@JvmStatic
|
||||
@JsName("fromId")
|
||||
@JvmName("fromId")
|
||||
operator fun invoke(imageId: String): Image = when (imageId.length) {
|
||||
37 -> NotOnlineImageFromFile(imageId) // /f8f1ab55-bf8e-4236-b55e-955848d7069f
|
||||
|
@ -13,6 +13,7 @@ package net.mamoe.mirai.message.data
|
||||
|
||||
import net.mamoe.mirai.contact.Contact
|
||||
import net.mamoe.mirai.contact.sendMessage
|
||||
import kotlin.jvm.JvmSynthetic
|
||||
|
||||
/**
|
||||
* 可发送的或从服务器接收的消息.
|
||||
@ -82,6 +83,7 @@ interface Message {
|
||||
* println(c)// "Hello world!"
|
||||
* ```
|
||||
*/
|
||||
@JvmSynthetic // in java they should use `plus` instead
|
||||
fun followedBy(tail: Message): MessageChain {
|
||||
require(tail !is SingleOnly) { "SingleOnly Message cannot follow another message" }
|
||||
require(this !is SingleOnly) { "SingleOnly Message cannot be followed" }
|
||||
@ -92,6 +94,7 @@ interface Message {
|
||||
override fun toString(): String
|
||||
|
||||
operator fun plus(another: Message): MessageChain = this.followedBy(another)
|
||||
|
||||
operator fun plus(another: String): MessageChain = this.followedBy(another.toMessage())
|
||||
// `+ ""` will be resolved to `plus(String)` instead of `plus(CharSeq)`
|
||||
operator fun plus(another: CharSequence): MessageChain = this.followedBy(another.toString().toMessage())
|
||||
|
@ -7,6 +7,9 @@
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
@file:JvmMultifileClass
|
||||
@file:JvmName("MessageUtils")
|
||||
|
||||
package net.mamoe.mirai.message.data
|
||||
|
||||
import net.mamoe.mirai.message.data.NullMessageChain.toString
|
||||
@ -14,6 +17,9 @@ import net.mamoe.mirai.utils.MiraiExperimentalAPI
|
||||
import kotlin.contracts.ExperimentalContracts
|
||||
import kotlin.contracts.contract
|
||||
import kotlin.js.JsName
|
||||
import kotlin.jvm.JvmMultifileClass
|
||||
import kotlin.jvm.JvmName
|
||||
import kotlin.jvm.JvmSynthetic
|
||||
import kotlin.jvm.Volatile
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
@ -35,10 +41,12 @@ interface MessageChain : Message, MutableList<Message> {
|
||||
override fun followedBy(tail: Message): MessageChain
|
||||
// endregion
|
||||
|
||||
@JvmSynthetic
|
||||
operator fun plusAssign(message: Message) {
|
||||
this.followedBy(message)
|
||||
}
|
||||
|
||||
@JvmSynthetic // make java user happier
|
||||
operator fun plusAssign(plain: String) {
|
||||
this.plusAssign(plain.toMessage())
|
||||
}
|
||||
@ -53,7 +61,7 @@ interface MessageChain : Message, MutableList<Message> {
|
||||
operator fun <M : Message> get(key: Message.Key<M>): M = first(key)
|
||||
|
||||
override fun eq(other: Message): Boolean {
|
||||
if(other is MessageChain && other.size != this.size)
|
||||
if (other is MessageChain && other.size != this.size)
|
||||
return false
|
||||
return this.toString() == other.toString()
|
||||
}
|
||||
@ -67,13 +75,16 @@ inline operator fun <reified T : Message> MessageChain.getValue(thisRef: Any?, p
|
||||
/**
|
||||
* 构造无初始元素的可修改的 [MessageChain]. 初始大小将会被设定为 8
|
||||
*/
|
||||
@JsName("emptyMessageChain")
|
||||
@JvmName("newChain")
|
||||
@JsName("newChain")
|
||||
@Suppress("FunctionName")
|
||||
fun MessageChain(): MessageChain = EmptyMessageChain()
|
||||
|
||||
/**
|
||||
* 构造无初始元素的可修改的 [MessageChain]. 初始大小将会被设定为 [initialCapacity]
|
||||
*/
|
||||
@JvmName("newChain")
|
||||
@JsName("newChain")
|
||||
@Suppress("FunctionName")
|
||||
fun MessageChain(initialCapacity: Int): MessageChain =
|
||||
if (initialCapacity == 0) EmptyMessageChain()
|
||||
@ -83,6 +94,8 @@ fun MessageChain(initialCapacity: Int): MessageChain =
|
||||
* 构造 [MessageChain]
|
||||
* 若仅提供一个参数, 请考虑使用 [Message.toChain] 以优化性能
|
||||
*/
|
||||
@JvmName("newChain")
|
||||
@JsName("newChain")
|
||||
@Suppress("FunctionName")
|
||||
fun MessageChain(vararg messages: Message): MessageChain =
|
||||
if (messages.isEmpty()) EmptyMessageChain()
|
||||
@ -91,6 +104,8 @@ fun MessageChain(vararg messages: Message): MessageChain =
|
||||
/**
|
||||
* 构造 [MessageChain]
|
||||
*/
|
||||
@JvmName("newChain")
|
||||
@JsName("newChain")
|
||||
@Suppress("FunctionName")
|
||||
fun MessageChain(messages: Iterable<Message>): MessageChain =
|
||||
MessageChainImpl(messages.toMutableList())
|
||||
@ -106,6 +121,8 @@ fun MessageChain(messages: Iterable<Message>): MessageChain =
|
||||
*
|
||||
* @see Message.toChain receiver 模式
|
||||
*/
|
||||
@JvmName("newSingleMessageChain")
|
||||
@JsName("newChain")
|
||||
@MiraiExperimentalAPI
|
||||
@UseExperimental(ExperimentalContracts::class)
|
||||
@Suppress("FunctionName")
|
||||
@ -301,7 +318,7 @@ internal inline class MessageChainImpl constructor(
|
||||
constructor(vararg messages: Message) : this(messages.toMutableList())
|
||||
|
||||
// region Message override
|
||||
override fun toString(): String = this.delegate.joinToString("") { it.toString() }
|
||||
override fun toString(): String = this.delegate.joinToString("") { it.toString() }
|
||||
|
||||
override operator fun contains(sub: String): Boolean = delegate.any { it.contains(sub) }
|
||||
override fun followedBy(tail: Message): MessageChain {
|
||||
@ -352,6 +369,7 @@ internal inline class SingleMessageChainImpl(
|
||||
|
||||
// region Message override
|
||||
override operator fun contains(sub: String): Boolean = delegate.contains(sub)
|
||||
|
||||
override fun followedBy(tail: Message): MessageChain {
|
||||
require(tail !is SingleOnly) { "SingleOnly Message cannot follow another message" }
|
||||
return if (tail is MessageChain) tail.apply { followedBy(delegate) }
|
||||
|
@ -7,8 +7,14 @@
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
@file:JvmMultifileClass
|
||||
@file:JvmName("MessageUtils")
|
||||
|
||||
package net.mamoe.mirai.message.data
|
||||
|
||||
import kotlin.jvm.JvmMultifileClass
|
||||
import kotlin.jvm.JvmName
|
||||
|
||||
/**
|
||||
* 消息源, 用于被引用. 它将由协议模块实现.
|
||||
* 消息源只用于 [QuoteReply]
|
||||
@ -18,7 +24,7 @@ package net.mamoe.mirai.message.data
|
||||
* @see MessageSource.quote 引用这条消息, 创建 [MessageChain]
|
||||
*/
|
||||
interface MessageSource : Message {
|
||||
companion object : Message.Key<MessageSource>
|
||||
companion object Key : Message.Key<MessageSource>
|
||||
|
||||
/**
|
||||
* 实际上是个随机数, 但服务器确实是用它当做 uid
|
||||
|
@ -7,9 +7,19 @@
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
@file:JvmMultifileClass
|
||||
@file:JvmName("MessageUtils")
|
||||
|
||||
package net.mamoe.mirai.message.data
|
||||
|
||||
import kotlin.jvm.JvmMultifileClass
|
||||
import kotlin.jvm.JvmName
|
||||
|
||||
/**
|
||||
* 纯文本. 可含 emoji 表情.
|
||||
*
|
||||
* 一般不需要主动构造 [PlainText], [Message] 可直接与 [String] 相加. Java 用户请使用 [MessageChain.plus]
|
||||
*/
|
||||
inline class PlainText(val stringValue: String) : Message {
|
||||
override operator fun contains(sub: String): Boolean = sub in stringValue
|
||||
override fun toString(): String = stringValue
|
||||
@ -17,7 +27,7 @@ inline class PlainText(val stringValue: String) : Message {
|
||||
companion object Key : Message.Key<PlainText>
|
||||
|
||||
override fun eq(other: Message): Boolean {
|
||||
if(other is MessageChain){
|
||||
if (other is MessageChain) {
|
||||
return other eq this.toString()
|
||||
}
|
||||
return other is PlainText && other.stringValue == this.stringValue
|
||||
|
@ -7,10 +7,15 @@
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
@file:JvmMultifileClass
|
||||
@file:JvmName("MessageUtils")
|
||||
|
||||
package net.mamoe.mirai.message.data
|
||||
|
||||
import net.mamoe.mirai.contact.Member
|
||||
import net.mamoe.mirai.utils.MiraiInternalAPI
|
||||
import kotlin.jvm.JvmMultifileClass
|
||||
import kotlin.jvm.JvmName
|
||||
|
||||
|
||||
/**
|
||||
|
@ -7,10 +7,17 @@
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
@file:JvmMultifileClass
|
||||
@file:JvmName("MessageUtils")
|
||||
|
||||
@file:Suppress("MemberVisibilityCanBePrivate")
|
||||
|
||||
package net.mamoe.mirai.message.data
|
||||
|
||||
import net.mamoe.mirai.utils.MiraiExperimentalAPI
|
||||
import kotlin.jvm.JvmMultifileClass
|
||||
import kotlin.jvm.JvmName
|
||||
|
||||
/**
|
||||
* XML 消息, 如分享, 卡片等.
|
||||
*
|
||||
@ -30,6 +37,7 @@ inline class XMLMessage(val stringValue: String) : Message,
|
||||
* 构造一条 XML 消息
|
||||
*/
|
||||
@XMLDsl
|
||||
@MiraiExperimentalAPI("还未支持")
|
||||
inline fun buildXMLMessage(block: @XMLDsl XMLMessageBuilder.() -> Unit): XMLMessage =
|
||||
XMLMessage(XMLMessageBuilder().apply(block).text)
|
||||
|
||||
|
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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.utils
|
||||
|
||||
@MiraiInternalAPI
|
||||
@Suppress("EXTENSION_SHADOWED_BY_MEMBER")
|
||||
expect fun Throwable.addSuppressed(e: Throwable)
|
||||
|
||||
@MiraiInternalAPI
|
||||
@Suppress("DuplicatedCode")
|
||||
inline fun <R> tryNTimes(repeat: Int, block: () -> R): R {
|
||||
var lastException: Throwable? = null
|
||||
|
||||
repeat(repeat) {
|
||||
try {
|
||||
return block()
|
||||
} catch (e: Throwable) {
|
||||
if (lastException == null) {
|
||||
lastException = e
|
||||
}
|
||||
lastException!!.addSuppressed(e)
|
||||
}
|
||||
}
|
||||
|
||||
throw lastException!!
|
||||
}
|
||||
|
||||
@MiraiInternalAPI
|
||||
@Suppress("DuplicatedCode")
|
||||
inline fun <R> tryNTimesOrNull(repeat: Int, block: () -> R): R? {
|
||||
var lastException: Throwable? = null
|
||||
|
||||
repeat(repeat) {
|
||||
try {
|
||||
return block()
|
||||
} catch (e: Throwable) {
|
||||
if (lastException == null) {
|
||||
lastException = e
|
||||
}
|
||||
lastException!!.addSuppressed(e)
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
@MiraiInternalAPI
|
||||
@Suppress("DuplicatedCode")
|
||||
inline fun <R> tryNTimesOrException(repeat: Int, block: () -> R): Throwable? {
|
||||
var lastException: Throwable? = null
|
||||
|
||||
repeat(repeat) {
|
||||
try {
|
||||
block()
|
||||
return null
|
||||
} catch (e: Throwable) {
|
||||
if (lastException == null) {
|
||||
lastException = e
|
||||
}
|
||||
lastException!!.addSuppressed(e)
|
||||
}
|
||||
}
|
||||
|
||||
return lastException!!
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package net.mamoe.mirai.utils
|
||||
|
||||
private var isAddSuppressedSupported: Boolean = true
|
||||
|
||||
@MiraiInternalAPI
|
||||
@Suppress("EXTENSION_SHADOWED_BY_MEMBER")
|
||||
actual fun Throwable.addSuppressed(e: Throwable) {
|
||||
if (!isAddSuppressedSupported) {
|
||||
return
|
||||
}
|
||||
try {
|
||||
this.addSuppressed(e)
|
||||
} catch (e: Exception) {
|
||||
isAddSuppressedSupported = false
|
||||
}
|
||||
}
|
@ -2,9 +2,9 @@ apply plugin: "kotlin"
|
||||
apply plugin: "java"
|
||||
|
||||
dependencies {
|
||||
implementation files("../../mirai-core/build/classes/kotlin/jvm/main") // IDE bug
|
||||
runtimeOnly files("../../mirai-core/build/classes/kotlin/jvm/main") // IDE bug
|
||||
|
||||
implementation files("../../mirai-core-qqandroid/build/classes/kotlin/jvm/main") // IDE bug
|
||||
runtimeOnly files("../../mirai-core-qqandroid/build/classes/kotlin/jvm/main") // IDE bug
|
||||
implementation project(":mirai-core-qqandroid")
|
||||
|
||||
api group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib-jdk8', version: kotlinVersion
|
||||
|
@ -2,9 +2,9 @@ apply plugin: "java"
|
||||
apply plugin: "kotlin"
|
||||
|
||||
dependencies {
|
||||
implementation files("../../mirai-core/build/classes/kotlin/jvm/main") // IDE bug
|
||||
runtimeOnly files("../../mirai-core/build/classes/kotlin/jvm/main") // IDE bug
|
||||
|
||||
implementation files("../../mirai-core-qqandroid/build/classes/kotlin/jvm/main") // IDE bug
|
||||
runtimeOnly files("../../mirai-core-qqandroid/build/classes/kotlin/jvm/main") // IDE bug
|
||||
implementation project(":mirai-core-qqandroid")
|
||||
implementation project(":mirai-japt")
|
||||
}
|
||||
|
@ -5,6 +5,9 @@ import net.mamoe.mirai.japt.BlockingContacts;
|
||||
import net.mamoe.mirai.japt.BlockingQQ;
|
||||
import net.mamoe.mirai.japt.Events;
|
||||
import net.mamoe.mirai.message.GroupMessage;
|
||||
import net.mamoe.mirai.message.data.At;
|
||||
import net.mamoe.mirai.message.data.Image;
|
||||
import net.mamoe.mirai.message.data.MessageUtils;
|
||||
|
||||
class BlockingTest {
|
||||
|
||||
@ -19,8 +22,14 @@ class BlockingTest {
|
||||
|
||||
Events.subscribeAlways(GroupMessage.class, (GroupMessage message) -> {
|
||||
final BlockingQQ sender = BlockingContacts.createBlocking(message.getSender());
|
||||
sender.sendMessage("Hello World!");
|
||||
System.out.println("发送完了");
|
||||
|
||||
sender.sendMessage("Hello");
|
||||
sender.sendMessage(MessageUtils.newChain()
|
||||
.plus(new At(message.getSender()))
|
||||
.plus(Image.fromId("{xxxx}.jpg"))
|
||||
.plus("123465")
|
||||
);
|
||||
});
|
||||
|
||||
Thread.sleep(999999999);
|
||||
|
@ -32,7 +32,7 @@ Mirai Java Apt
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>net.mamoe</groupId>
|
||||
<artifactId>mirai-core-qqandroid-jvm</artifactId>
|
||||
<artifactId>mirai-core-qqandroid</artifactId>
|
||||
<version>CORE_VERSION</version> <!-- 替换版本为最新版本 -->
|
||||
</dependency>
|
||||
|
||||
@ -51,7 +51,7 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("net.mamoe:mirai-core-qqandroid-jvm:CORE_VERSION")
|
||||
implementation("net.mamoe:mirai-core-qqandroid:CORE_VERSION")
|
||||
implementation("net.mamoe:mirai-japt:JAPT_VERSION")
|
||||
}
|
||||
```
|
||||
|
@ -18,9 +18,9 @@ buildscript {
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
java
|
||||
id("com.jfrog.bintray") version "1.8.0"
|
||||
`maven-publish`
|
||||
// maven
|
||||
id("com.jfrog.bintray") version "1.8.0"
|
||||
}
|
||||
|
||||
val kotlinVersion: String by rootProject.ext
|
||||
@ -77,6 +77,10 @@ tasks.withType<JavaCompile>() {
|
||||
options.encoding = "UTF-8"
|
||||
}
|
||||
|
||||
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
|
||||
kotlinOptions.jvmTarget = "1.8"
|
||||
}
|
||||
|
||||
bintray {
|
||||
val keyProps = Properties()
|
||||
val keyFile = file("../keys.properties")
|
||||
|
@ -65,11 +65,11 @@ try{
|
||||
println("jdk版本为 "+ javaVersionNum)
|
||||
include(':mirai-console-graphical')
|
||||
} else {
|
||||
println("当前使用的 JDK 版本为 ${System.getProperty("java.version")}, 最低需要 JDK 11 才能引入模块 `:mirai-debug`")
|
||||
println("当前使用的 JDK 版本为 ${System.getProperty("java.version")}, 最低需要 JDK 11 才能引入模块 `:mirai-console-graphical`")
|
||||
}
|
||||
}
|
||||
}catch(Exception ignored){
|
||||
|
||||
println("无法确定 JDK 版本, 将不会引入 `:mirai-console-graphical`")
|
||||
}
|
||||
|
||||
enableFeaturePreview('GRADLE_METADATA')
|
Loading…
Reference in New Issue
Block a user