Merge remote-tracking branch 'mirai/dev' into dev

This commit is contained in:
Him188 2021-02-01 08:14:19 +08:00
commit 622cf31175
9 changed files with 141 additions and 38 deletions

View File

@ -12,7 +12,7 @@
import org.gradle.api.attributes.Attribute
object Versions {
const val project = "2.2.2-dev-1"
const val project = "2.2.2"
const val core = project
const val console = project

View File

@ -11,7 +11,7 @@
| 版本类型 | 版本号 |
|:------:|:------------------------------:|
| 稳定 | 2.2.1 |
| 稳定 | 2.2.2 |
| 预览 | - |
| 开发 | [![Version]][Bintray Download] |
@ -40,7 +40,7 @@ repositories {
}
dependencies {
api("net.mamoe", "mirai-core", "2.2.1") // 替换为你需要的版本号
api("net.mamoe", "mirai-core", "2.2.2") // 替换为你需要的版本号
}
```
@ -64,7 +64,7 @@ repositories {
}
dependencies {
api('net.mamoe', 'mirai-core', '2.2.1') // 替换为你需要的版本号
api('net.mamoe', 'mirai-core', '2.2.2') // 替换为你需要的版本号
}
```
@ -77,7 +77,7 @@ dependencies {
mirai 在开发时需要 `net.mamoe:mirai-core-api`, 在运行时需要 `net.mamoe:mirai-core`。可以在开发和编译时只依赖 `mirai-core-api`,会减轻对 IDE 的负担。
```kotlin
dependencies {
val miraiVersion = "2.2.1" // 替换为你需要的版本号
val miraiVersion = "2.2.2" // 替换为你需要的版本号
api("net.mamoe", "mirai-core-api", miraiVersion) // 编译代码使用
runtimeOnly("net.mamoe", "mirai-core", miraiVersion) // 运行时使用
}
@ -105,7 +105,7 @@ dependencies {
<dependency>
<groupId>net.mamoe</groupId>
<artifactId>mirai-core-jvm</artifactId>
<version>2.2.1</version> <!-- 替换版本为你需要的版本 -->
<version>2.2.2</version> <!-- 替换版本为你需要的版本 -->
</dependency>
</dependencies>
```

View File

@ -64,7 +64,7 @@ Listener listener = GlobalEventChannel.INSTANCE.subscribeAlways(GroupMessageEven
listener.complete(); // 停止监听
```
异常默认会被相关 Bot 日志记录。可以在 `subscribeAlways` 添加如下内容来处理异常。
异常默认会被相关 Bot 日志记录。可以在 `subscribeAlways` 之前添加如下内容来处理异常。
```
// Kotlin
.exceptionHandler { e -> e.printStackTrace() }
@ -73,12 +73,14 @@ listener.complete(); // 停止监听
.exceptionHandler(e -> e.printStackTrace())
```
**`GlobalEventChannel` 会监听到来自所有 `Bot` 的事件,如果只希望监听某一个,请使用 `bot.eventChannel`。**
**`GlobalEventChannel` 会监听到来自所有 `Bot` 的事件,如果只希望监听某一个 bot,请使用 `bot.eventChannel`。**
> 现在你可以继续阅读,或跳到下一章 [Messages](Messages.md)
> 现在你可以继续阅读详细了解事件,或:
>
> 回到 [目录](#目录)
> [回到 Mirai 文档索引](README.md#mirai-core-api-文档)
> - 跳到下一章 [Messages](Messages.md)
> - [查看事件列表](../mirai-core-api/src/commonMain/kotlin/event/events/README.md#事件)
> - 回到 [目录](#目录)
> - [回到 Mirai 文档索引](README.md#mirai-core-api-文档)
## 事件通道

View File

@ -4,16 +4,16 @@
- [消息系统](#消息系统)
- [消息类型](#消息类型)
- [消息元素](#消息元素)
- [转义规则](#转义规则)
- [消息链的 mirai 码](#消息链的-mirai-码)
- [由 `CodableMessage` 取得 mirai 码字符串](#由-codablemessage-取得-mirai-码字符串)
- [由 mirai 码字符串取得 `MessageChain` 实例](#由-mirai-码字符串取得-messagechain-实例)
- [消息链](#消息链)
- [发送消息](#发送消息)
- [构造消息链](#构造消息链)
- [元素唯一性](#元素唯一性)
- [获取消息链中的消息元素](#获取消息链中的消息元素)
- [Mirai 码](#mirai-码)
- [转义规则](#转义规则)
- [消息链的 mirai 码](#消息链的-mirai-码)
- [由 `CodableMessage` 取得 mirai 码字符串](#由-codablemessage-取得-mirai-码字符串)
- [由 mirai 码字符串取得 `MessageChain` 实例](#由-mirai-码字符串取得-messagechain-实例)
## 消息系统
@ -28,16 +28,31 @@
## 消息类型
*单个消息元素(`SingleMessage`*分为 *消息内容(`MessageContent`**消息元数据(`MessageMetadata`*
Mirai 支持富文本消息。
*单个消息元素(`SingleMessage`* 分为 *内容(`MessageContent`**元数据(`MessageMetadata`*
实践中,消息内容和消息元数据会混合存在于消息链中。
### 内容
*内容(`MessageContent`* 即为 *纯文本*、*提及某人*、*图片*、*语音* 和 *音乐分享* 等**有内容**的数据,一条消息中必须包含内容才能发送。
### 元数据
*元数据(`MessageMetadata`* 包含 *来源*、*引用回复* 和 *秀图标识* 等。
- *消息来源*`MessageSource`)存在于每条消息中,包含唯一识别信息,用于撤回和引用回复的定位。
- *引用回复*`QuoteReply`)若存在,则会在客户端中解析为本条消息引用了另一条消息。
- *秀图标识*`ShowImageFlag`若存在则表明这条消息中的图片是以秀图发送QQ 的一个功能)。
元数据与内容的区分就在于,一条消息没有元数据也能显示,但一条消息不能没有内容。**元数据是消息的属性**。
> 回到 [目录](#目录)
## 消息元素
Mirai 支持富文本消息。
消息拥有三种转换到字符串的表示方式。
| 方法 | 解释 |
@ -104,7 +119,15 @@ Mirai 支持富文本消息。
suspend fun sendMessage(message: Message): MessageReceipt<Contact>
```
要发送简单的单元素消息,使用:
要发送字符串消息,使用:(第一部分是 Kotlin随后是 Java下同
```kotlin
contact.sendMessage("Hello!")
```
```java
contact.sendMessage("Hello!");
```
发送字符串实际上是在发送纯文本消息。上面的代码相当于:
```kotlin
contact.sendMessage(PlainText("Hello!"))
```
@ -114,10 +137,10 @@ contact.sendMessage(new PlainText("Hello!"));
要发送多元素消息,可将消息使用 `plus` 操作连接:
```kotlin
contact.sendMessage(PlainText("你要的图片是") + Image("/f8f1ab55-bf8e-4236-b55e-955848d7069f")) // 一个纯文本加一个图片
contact.sendMessage(PlainText("你要的图片是") + Image("{f8f1ab55-bf8e-4236-b55e-955848d7069f}.png")) // 一个纯文本加一个图片
```
```java
contact.sendMessage(new PlainText("你要的图片是:").plus(Image.fromId("/f8f1ab55-bf8e-4236-b55e-955848d7069f"))); // 一个纯文本加一个图片
contact.sendMessage(new PlainText("你要的图片是:").plus(Image.fromId("{f8f1ab55-bf8e-4236-b55e-955848d7069f}.png"))); // 一个纯文本加一个图片
```
### 构造消息链
@ -165,13 +188,18 @@ val chain = buildMessageChain {
方法都位于 `net.mamoe.mirai.message.data.MessageUtils`
使用 `newChain`
```java
MessageChain chain = MessageUtils.newChain(new PlainText("Hello"), Image.fromId("{f8f1ab55-bf8e-4236-b55e-955848d7069f}.png"));
```
使用 `MessageChainBuilder`:
```java
MessageChain chain = new MessageChainBuilder()
.append(new PlainText("string"))
.append("string") // 会被构造成 PlainText 再添加, 相当于上一行
.append(AtAll.INSTANCE)
.append(Image.fromId("/f8f1ab55-bf8e-4236-b55e-955848d7069f"))
.append(Image.fromId("{f8f1ab55-bf8e-4236-b55e-955848d7069f}.png"))
.build();
```

View File

@ -66,7 +66,7 @@
**为了避免遇到各种问题,请仔细阅读。**
1. [JVM 环境和开发准备工作2 分钟)](Preparations.md#mirai---preparations)
2. **配置项目依赖**
2. **配置项目依赖** (二选一)
- 要把 mirai-core 嵌入一个应用使用,请阅读 [配置项目依赖](ConfiguringProjects.md)。
- 要为 [`mirai-console`] 框架开发插件,请阅读 [mirai-console 的配置插件项目](https://github.com/mamoe/mirai-console/blob/master/docs/ConfiguringProjects.md)。
3. 阅读 API 文档(见下文)

@ -1 +1 @@
Subproject commit 24c0482d50a0d1314a9e07dd70b428c94d3df25a
Subproject commit a5481accb5f882d121ff9fc1d55e4e5f3e908e76

View File

@ -156,6 +156,7 @@ internal class GroupImpl(
is ImgStore.GroupPicUp.Response.FileExists -> {
val resourceId = resource.calculateResourceId()
return OfflineGroupImage(imageId = resourceId)
.also { it.fileId = response.fileId.toInt() }
.also { ImageUploadEvent.Succeed(this@GroupImpl, resource, it).broadcast() }
}
is ImgStore.GroupPicUp.Response.RequireUpload -> {
@ -169,6 +170,7 @@ internal class GroupImpl(
)
return OfflineGroupImage(imageId = resource.calculateResourceId())
.also { it.fileId = response.fileId.toInt() }
.also { ImageUploadEvent.Succeed(this@GroupImpl, resource, it).broadcast() }
}
}

View File

@ -25,11 +25,11 @@ import net.mamoe.mirai.internal.network.protocol.packet.chat.image.ImgStore
import net.mamoe.mirai.internal.network.protocol.packet.chat.receive.*
import net.mamoe.mirai.internal.network.protocol.packet.chat.receive.createToFriend
import net.mamoe.mirai.internal.network.protocol.packet.chat.receive.createToGroup
import net.mamoe.mirai.internal.network.protocol.packet.sendAndExpect
import net.mamoe.mirai.message.MessageReceipt
import net.mamoe.mirai.message.data.*
import net.mamoe.mirai.utils.castOrNull
import net.mamoe.mirai.utils.currentTimeSeconds
import java.lang.UnsupportedOperationException
/**
* 通用处理器
@ -105,7 +105,7 @@ internal abstract class SendMessageHandler<C : Contact> {
*/
suspend fun sendMessagePacket(
originalMessage: Message,
transformedMessage: Message,
transformedMessage: MessageChain,
finalMessage: MessageChain,
step: SendMessageStep,
): MessageReceipt<C> {
@ -125,10 +125,10 @@ internal abstract class SendMessageHandler<C : Contact> {
if (resp is MessageSvcPbSendMsg.Response.MessageTooLarge) {
return when (step) {
SendMessageStep.FIRST -> {
sendMessage(originalMessage, transformedMessage, SendMessageStep.LONG_MESSAGE)
sendMessageImpl(originalMessage, transformedMessage, SendMessageStep.LONG_MESSAGE)
}
SendMessageStep.LONG_MESSAGE -> {
sendMessage(originalMessage, transformedMessage, SendMessageStep.FRAGMENTED)
sendMessageImpl(originalMessage, transformedMessage, SendMessageStep.FRAGMENTED)
}
else -> {
@ -219,6 +219,8 @@ internal abstract class SendMessageHandler<C : Contact> {
)
}
open suspend fun preConversionTransformedMessage(message: Message): Message = message
open suspend fun conversionMessageChain(chain: MessageChain): MessageChain = chain
open suspend fun postTransformActions(chain: MessageChain) {
@ -255,15 +257,33 @@ internal suspend fun <C : Contact> SendMessageHandler<C>.transformSpecialMessage
}
/**
* Might be recalled with [transformedMessage] `is` [LongMessageInternal] if length estimation failed (sendMessagePacket)
* Send a message, and covert messages
*
* Don't recall this function.
*/
internal suspend fun <C : Contact> SendMessageHandler<C>.sendMessage(
originalMessage: Message,
transformedMessage: Message,
step: SendMessageStep,
): MessageReceipt<C> = sendMessageImpl(
originalMessage,
conversionMessageChain(
transformSpecialMessages(
preConversionTransformedMessage(transformedMessage)
)
),
step
)
/**
* Might be recalled with [transformedMessage] `is` [LongMessageInternal] if length estimation failed (sendMessagePacket)
*/
internal suspend fun <C : Contact> SendMessageHandler<C>.sendMessageImpl(
originalMessage: Message,
transformedMessage: MessageChain,
step: SendMessageStep,
): MessageReceipt<C> { // Result cannot be in interface.
val chain = transformSpecialMessages(transformedMessage)
.convertToLongMessageIfNeeded(step)
val chain = transformedMessage.convertToLongMessageIfNeeded(step)
chain.findIsInstance<QuoteReply>()?.source?.ensureSequenceIdAvailable()
@ -314,11 +334,19 @@ internal class GroupSendMessageHandler(
override val senderName: String
get() = contact.botAsMember.nameCardOrNick
override suspend fun postTransformActions(chain: MessageChain) {
chain.asSequence().filterIsInstance<FriendImage>().forEach { image ->
contact.updateFriendImageForGroupMessage(image)
override suspend fun conversionMessageChain(chain: MessageChain): MessageChain = chain.map { element ->
when (element) {
is OfflineGroupImage -> {
contact.fixImageFileId(element)
element
}
is FriendImage -> {
contact.updateFriendImageForGroupMessage(element)
}
else -> element
}
}
}.toMessageChain()
override suspend fun constructSourceFromMusicShareResponse(
finalMessage: MessageChain,
@ -341,18 +369,55 @@ internal class GroupSendMessageHandler(
}
companion object {
private suspend fun GroupImpl.fixImageFileId(image: OfflineGroupImage) {
if (image.fileId == null) {
val response: ImgStore.GroupPicUp.Response = ImgStore.GroupPicUp(
bot.client,
uin = bot.id,
groupCode = this.id,
md5 = image.md5,
size = 1,
).sendAndExpect(bot)
when (response) {
is ImgStore.GroupPicUp.Response.Failed -> {
image.fileId = 0 // Failed
}
is ImgStore.GroupPicUp.Response.FileExists -> {
image.fileId = response.fileId.toInt()
}
is ImgStore.GroupPicUp.Response.RequireUpload -> {
image.fileId = response.fileId.toInt()
}
}
}
}
/**
* Ensures server holds the cache
*/
private suspend fun GroupImpl.updateFriendImageForGroupMessage(image: FriendImage) {
private suspend fun GroupImpl.updateFriendImageForGroupMessage(image: FriendImage): OfflineGroupImage {
bot.network.run {
ImgStore.GroupPicUp(
val response = ImgStore.GroupPicUp(
bot.client,
uin = bot.id,
groupCode = id,
md5 = image.md5,
size = if (image is OnlineFriendImageImpl) image.delegate.fileLen else 0
).sendAndExpect<ImgStore.GroupPicUp.Response>()
return OfflineGroupImage(image.imageId).also { img ->
when (response) {
is ImgStore.GroupPicUp.Response.FileExists -> {
img.fileId = response.fileId.toInt()
}
is ImgStore.GroupPicUp.Response.RequireUpload -> {
img.fileId = response.fileId.toInt()
}
is ImgStore.GroupPicUp.Response.Failed -> {
img.fileId = 0
}
}
}
}
}
}

View File

@ -178,6 +178,7 @@ internal fun ImMsgBody.CustomFace.toNotOnlineImage(): ImMsgBody.NotOnlineImage {
@Suppress("DEPRECATION")
internal fun OfflineGroupImage.toJceData(): ImMsgBody.CustomFace {
return ImMsgBody.CustomFace(
fileId = this.fileId ?: 0,
filePath = this.imageId,
picMd5 = this.md5,
flag = ByteArray(4),
@ -185,7 +186,9 @@ internal fun OfflineGroupImage.toJceData(): ImMsgBody.CustomFace {
//_400Url = "/gchatpic_new/000000000/1041235568-2195821338-01E9451B70EDEAE3B37C101F1EEBF5B5/400?term=2",
//_400Width = 351,
oldData = oldData,
// pbReserve = CustomFaceExtPb.ResvAttr().toByteArray(CustomFaceExtPb.ResvAttr.serializer())
// pbReserve = "08 00 10 00 32 00 50 00 78 08".autoHexToBytes(),
// useful = 1,
// pbReserve = CustomFaceExtPb.ResvAttr().toByteArray(CustomFaceExtPb.ResvAttr.serializer())
)
}
@ -258,6 +261,9 @@ internal interface OfflineImage : Image
internal data class OfflineGroupImage(
override val imageId: String
) : GroupImage(), OfflineImage, DeferredOriginUrlAware {
@Transient
internal var fileId: Int? = null
object Serializer : Image.FallbackSerializer("OfflineGroupImage")
override fun getUrl(bot: Bot): String {