# OneBot 消息内容的数据结构

消息内容有文本、语音、图片等多种类型，需要特定的数据结构来表示。在 OneBot 标准中主要有两种格式，来表示消息内容的数据结构：字符串（string）和数组（array）。

在 melobot 中，你不需要手动处理消息内容的数据结构，**但是了解消息内容的数据结构，有助于你使用 melobot 提供的相关方法**。

```{admonition} 注意
:class: caution
本页以下部分为保证阅读流畅性，直接复制了来源于 [OneBot 标准](https://github.com/botuniverse/onebot-11)的一些内容。
```

## 字符串格式

在字符串格式中，无论纯文本还是图片、表情、链接分享等多媒体内容都放在同一个字符串里，即，一条消息对应一个字符串。以下是一个字符串格式消息的例子：

```
[CQ:face,id=178]看看我刚拍的照片[CQ:image,file=123.jpg]
```

### CQ 码格式

消息中的多媒体内容使用 CQ 码来表示，形如 `[CQ:face,id=178]`。其中，`[CQ:]` 是固定格式；`face` 是「功能名」，除了 `face` 还有许多不同的功能名；`id=178` 是「参数」，某些功能不需要参数，而另一些需要多个参数，当有多个参数时，参数间使用逗号分隔。

一些 CQ 码的例子如下：

```
[CQ:shake]
[CQ:face,id=178]
[CQ:share,title=标题,url=http://baidu.com]
```

更多 CQ 码功能请参考 [消息段类型](https://github.com/botuniverse/onebot-11/blob/master/message/segment.md)。


```{admonition} 注意
:class: caution

CQ 码中不应有多余的空格，例如不应该使用 `[CQ:face, id=178]`。

CQ 码的参数值可以包含空格、换行、除 `[],&` 之外的特殊符号等。在解析时，应直接取 `[CQ:` 后、第一个 `,` 或 `]` 前的部分为功能名，第一个 `,` 之后到 `]` 之间的部分为参数，按 `,` 分割后，每个部分第一个 `=` 前的内容为参数名，之后的部分为参数值。例如 `[CQ:share,title=标题中有=等号,url=http://baidu.com]` 中，功能名为 `share`，`title` 参数值为 `标题中有=等号`，`url` 参数值为 `http://baidu.com`。
```

### CQ 码转义

CQ 码中包含一些特殊字符：`[`、`]`、`,` 等，而 CQ 码又是可能混杂在纯文本内容之中的，因此消息中的纯文本内容需要对特殊字符进行转义，以避免歧义。具体的转义规则如下：

| 转义前 | 转义后 |
| --- | --- |
| `&` | `&amp;` |
| `[` | `&#91;` |
| `]` | `&#93;` |

例如，一个纯文本消息转义前内容如下：

```
- [x] 使用 `&data` 获取地址
```

转义后如下：

```
- &#91;x&#93; 使用 `&amp;data` 获取地址
```

另一方面，CQ 码内部的参数值也可能出现特殊字符，也是需要转义的。由于 `,`（半角逗号）在 CQ 码中用于分隔参数，因此除了上面的转义规则，还需要对 `,` 进行转义，如下：

| 转义前 | 转义后 |
| --- | --- |
| `&` | `&amp;` |
| `[` | `&#91;` |
| `]` | `&#93;` |
| `,` | `&#44;` |

例如，一个链接分享消息的 CQ 码可能如下：

```
[CQ:share,title=震惊&#44;小伙睡觉前居然...,url=http://baidu.com/?a=1&amp;b=2]
```

## 数组格式

数组格式将消息表示为一系列消息段对象的数组，在基本语义上与字符串格式等价，可以相互转换，但数组格式的表达能力更强，例如可以嵌套、规定参数数据类型等。

### 消息段

在字符串格式中，使用 CQ 码表示多媒体内容，例如 `[CQ:image,file=123.jpg]`，这里 CQ 码功能名为 `image`，参数为 `file=123.jpg`，也即一个键值对。

消息段是 CQ 码在数组格式中的表示形式，基本格式如下：

```json
{
    "type": "image",
    "data": {
        "file": "123.jpg"
    }
}
```

其中 `type` 字段的类型为字符串，对应 CQ 码中的「功能名」；`data` 字段的类型为对象，对应 CQ 码的「参数」，此字段可为 null。**目前，除了合并转发相关的特殊消息段外，几乎所有消息段参数值类型均为字符串，以支持与 CQ 码的相互转换**。

**由于消息段不会将纯文本和多媒体内容放在一起，也就意味着任意一个字段的值都是真实值，而不再需要转义。**

为了使用消息段表示纯文本，引入一个特殊消息段类型 `text`，并在 `data` 中使用 `text` 字段来指示纯文本内容，例如：

```json
{
    "type": "text",
    "data": {
        "text": "这是一段纯文本"
    }
}
```

在将上面的消息段转成 CQ 码时，将会直接变成纯文本字符串，而不是真的转成 CQ 码。

更多消息段类型请参考 [消息段类型](https://github.com/botuniverse/onebot-11/blob/master/message/segment.md)。

### 消息段数组

了解了消息段概念之后，就不难理解消息的数组格式了，即消息段组成的数组。

例如，字符串格式下的这样一条消息：

```
&#91;第一部分&#93;[CQ:image,file=123.jpg]图片之后的部分，表情：[CQ:face,id=123]
```

表示成数组格式即为：

```json
[
    {
        "type": "text",
        "data": {
            "text": "[第一部分]"
        }
    },
    {
        "type": "image",
        "data": {
            "file": "123.jpg"
        }
    },
    {
        "type": "text",
        "data": {
            "text": "图片之后的部分，表情："
        }
    },
    {
        "type": "face",
        "data": {
            "id": "123"
        }
    }
]
```

## melobot 中的表示

在 melobot 中，都是使用消息段来表示消息内容。但是你仍然可以将消息段转换为 cq 字符串，不过不推荐这样做，会存在一定的注入风险。
