# ChatLab Standardized Chat Data Exchange Format

> v0.0.2

ChatLab defines a standardized data exchange format for chat records to support unified import and analysis across multiple platforms.

As long as you convert chat records into this format, ChatLab can parse them and use its analysis capabilities.

::: warning Note
This format specification is still in an early stage. Some fields and structures may be adjusted in future versions.
:::

## Overview

### Supported File Formats

| Format    | Extension | Use Case                                                       |
| --------- | --------- | -------------------------------------------------------------- |
| **JSON**  | `.json`   | Small to medium records (<1M messages), clear and readable     |
| **JSONL** | `.jsonl`  | Very large records (>1M messages), streaming, constant memory  |

### Format Comparison

| Feature         | JSON                        | JSONL                           |
| --------------- | --------------------------- | ------------------------------- |
| Memory Usage    | Requires loading all data   | Line-by-line, constant (~100MB) |
| File Size Limit | ~1GB (depends on memory)    | No practical limit              |
| Append Writing  | Requires rewriting the file | Direct line append              |
| Error Recovery  | One error breaks the file   | Bad lines can be skipped        |
| Readability     | Easy to read                | One record per line             |
| Recommended For | Small/medium (<1M records)  | Large records (>1M records)     |

## Quick Start

Here is a **minimal** ChatLab format example with only the required fields:

```json
{
  "chatlab": {
    "version": "0.0.2",
    "exportedAt": 1703001600
  },
  "meta": {
    "name": "My Group Chat",
    "platform": "qq",
    "type": "group"
  },
  "members": [
    {
      "platformId": "123456",
      "accountName": "John"
    }
  ],
  "messages": [
    {
      "sender": "123456",
      "accountName": "John",
      "timestamp": 1703001600,
      "type": 0,
      "content": "Hello everyone!"
    }
  ]
}
```

---

## JSON Format Details

### File Header (chatlab)

| Field         | Type   | Required | Description                             |
| ------------- | ------ | -------- | --------------------------------------- |
| `version`     | string | ✅       | Format version, currently `"0.0.2"`     |
| `exportedAt`  | number | ✅       | Export time (Unix timestamp in seconds) |
| `generator`   | string | -        | Name of the generator tool              |
| `description` | string | -        | Description                             |

### Metadata (meta)

| Field         | Type   | Required | Description                                                     |
| ------------- | ------ | -------- | --------------------------------------------------------------- |
| `name`        | string | ✅       | Group or conversation name                                      |
| `platform`    | string | ✅       | Platform identifier, such as `qq` / `wechat` / `discord` / `whatsapp` |
| `type`        | string | ✅       | Chat type: `group` / `private`                                  |
| `groupId`     | string | -        | Group ID (group chat only)                                      |
| `groupAvatar` | string | -        | Group avatar (Data URL format)                                  |
| `ownerId`     | string | -        | Owner/exporter `platformId`                                     |

### Members (members)

| Field           | Type         | Required | Description                  |
| --------------- | ------------ | -------- | ---------------------------- |
| `platformId`    | string       | ✅       | Unique user identifier       |
| `accountName`   | string       | ✅       | Account name                 |
| `groupNickname` | string       | -        | Group nickname (group only)  |
| `aliases`       | string[]     | -        | User-defined aliases         |
| `avatar`        | string       | -        | User avatar (Data URL format) |
| `roles`         | MemberRole[] | -        | Member roles (multiple allowed) |

#### Roles (roles)

Members can have one or more roles to indicate identities such as owner or admin.

| Field  | Type   | Required | Description                                |
| ------ | ------ | -------- | ------------------------------------------ |
| `id`   | string | ✅       | Role ID: `owner` / `admin` / custom ID     |
| `name` | string | -        | Display name for the role (required for custom roles) |

**Standard role IDs:**

| ID      | Description      |
| ------- | ---------------- |
| `owner` | Group owner/creator |
| `admin` | Administrator    |

**Role examples:**

```json
// Group owner
"roles": [{ "id": "owner" }]

// Administrator
"roles": [{ "id": "admin" }]

// Multiple roles
"roles": [
  { "id": "owner" },
  { "id": "tech-team", "name": "Tech Team" },
  { "id": "vip", "name": "VIP Member" }
]
```

### Messages (messages)

| Field               | Type           | Required | Description                               |
| ------------------- | -------------- | -------- | ----------------------------------------- |
| `sender`            | string         | ✅       | Sender's `platformId`                     |
| `accountName`       | string         | ✅       | Account name at send time                 |
| `groupNickname`     | string         | -        | Group nickname at send time               |
| `timestamp`         | number         | ✅       | Unix timestamp in seconds                 |
| `type`              | number         | ✅       | Message type (see the table below)        |
| `content`           | string \| null | ✅       | Message content (`null` for non-text messages) |
| `platformMessageId` | string         | -        | Original platform message ID              |
| `replyToMessageId`  | string         | -        | Target message ID being replied to        |

#### Message IDs and Reply Relationships

**`platformMessageId`** (original platform message ID):

- Stores the unique identifier of a message on the source platform, such as a Discord snowflake ID or a QQ message ID
- Used together with `replyToMessageId` during queries to show the content of the replied-to message
- This field can be omitted if the platform does not provide message IDs

**`replyToMessageId`** (target message ID being replied to):

- Stores the **original platform ID** of the replied-to message
- By linking it with another message's `platformMessageId`, the replied-to message content and sender can be retrieved
- Only meaningful for reply-type messages
- This field can be omitted if the platform does not support replies or the data does not include reply relationships

---

## Message Type Reference

::: tip Tip
If your chat records contain other special types that should be supported, please submit an issue and we will evaluate whether to add them as standard message types.
:::

### Basic Message Types (0-19)

| Value | Name     | Description   |
| ----- | -------- | ------------- |
| 0     | TEXT     | Text message  |
| 1     | IMAGE    | Image         |
| 2     | VOICE    | Voice         |
| 3     | VIDEO    | Video         |
| 4     | FILE     | File          |
| 5     | EMOJI    | Sticker/emoji |
| 7     | LINK     | Link/card     |
| 8     | LOCATION | Location      |

### Interactive Message Types (20-39)

| Value | Name       | Description                   |
| ----- | ---------- | ----------------------------- |
| 20    | RED_PACKET | Red packet                    |
| 21    | TRANSFER   | Transfer                      |
| 22    | POKE       | Poke/nudge                    |
| 23    | CALL       | Voice/video call              |
| 24    | SHARE      | Share (music, mini app, etc.) |
| 25    | REPLY      | Quote reply                   |
| 26    | FORWARD    | Forwarded message             |
| 27    | CONTACT    | Contact card                  |

### System Message Types (80+)

| Value | Name   | Description                              |
| ----- | ------ | ---------------------------------------- |
| 80    | SYSTEM | System message (join/leave/announcement) |
| 81    | RECALL | Recalled message                         |
| 99    | OTHER  | Other/unknown                            |

## Avatar Format

The `avatar` and `groupAvatar` fields support two formats:

### 1. Data URL

An embedded format where the image data is encoded directly in the file, so it works offline:

```
data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD...
```

Supported image MIME types:

- `image/jpeg` - JPEG format (recommended, smaller size)
- `image/png` - PNG format
- `image/gif` - GIF format
- `image/webp` - WebP format

### 2. Network URL

An external-link format where the image is stored on a server. This keeps file size smaller but requires network access:

```
https://example.com/avatars/user123.jpg
```

::: tip Suggestion

- If you need offline access or long-term archiving, use the Data URL format
- When exporting Data URLs, compress avatars to 100x100 pixels or smaller to reduce file size
- If the avatar is hosted on a reliable long-lived CDN, you can use a network URL to keep the file smaller
:::

## Complete Examples

### Group Chat Example (with optional fields)

```json
{
  "chatlab": {
    "version": "0.0.2",
    "exportedAt": 1703001600,
    "generator": "My Converter Tool",
    "description": "2024 Tech Discussion Group Chat Backup"
  },
  "meta": {
    "name": "Tech Discussion Group",
    "platform": "wechat",
    "type": "group",
    "groupId": "38988428513",
    "groupAvatar": "data:image/jpeg;base64,/9j/4AAQSkZJRg...",
    "ownerId": "abc123"
  },
  "members": [
    {
      "platformId": "abc123",
      "accountName": "John",
      "groupNickname": "Owner-John",
      "avatar": "data:image/jpeg;base64,/9j/4AAQSkZJRg...",
      "roles": [{ "id": "owner" }]
    },
    {
      "platformId": "def456",
      "accountName": "Alice",
      "groupNickname": "Admin",
      "avatar": "data:image/jpeg;base64,/9j/4AAQSkZJRg...",
      "roles": [{ "id": "admin" }]
    }
  ],
  "messages": [
    {
      "platformMessageId": "msg_001",
      "sender": "abc123",
      "accountName": "John",
      "groupNickname": "Owner-John",
      "timestamp": 1703001600,
      "type": 0,
      "content": "Hello everyone! Welcome to the Tech Discussion Group."
    },
    {
      "platformMessageId": "msg_002",
      "sender": "def456",
      "accountName": "Alice",
      "groupNickname": "Admin",
      "timestamp": 1703001610,
      "type": 25,
      "content": "Received!",
      "replyToMessageId": "msg_001"
    }
  ]
}
```

### Private Chat Example

```json
{
  "chatlab": {
    "version": "0.0.2",
    "exportedAt": 1703001600
  },
  "meta": {
    "name": "Chat with Mike",
    "platform": "qq",
    "type": "private"
  },
  "members": [
    {
      "platformId": "123456789",
      "accountName": "Me",
      "avatar": "data:image/jpeg;base64,/9j/4AAQSkZJRg..."
    },
    {
      "platformId": "987654321",
      "accountName": "Mike",
      "avatar": "data:image/jpeg;base64,/9j/4AAQSkZJRg..."
    }
  ],
  "messages": [
    {
      "sender": "123456789",
      "accountName": "Me",
      "timestamp": 1703001600,
      "type": 0,
      "content": "Are you there?"
    }
  ]
}
```

## JSONL Streaming Format

JSONL (JSON Lines) is suitable for **very large chat records** (>1M messages) and can avoid memory overflow issues.

### Features

- One JSON object per line
- The `_type` field distinguishes line types: `header` / `member` / `message`
- Constant memory usage (about 100MB), suitable for GB-scale files
- Supports streaming writes, so data can be appended while exporting

### Line Types

| `_type`   | Description                         | Required              |
| --------- | ----------------------------------- | --------------------- |
| `header`  | File header containing `chatlab` and `meta` | ✅ Must be the first line |
| `member`  | Member information                  | - Optional            |
| `message` | Message record                      | ✅ At least one       |

### Complete Example

```jsonl
{"_type":"header","chatlab":{"version":"0.0.2","exportedAt":1703001600},"meta":{"name":"Tech Discussion Group","platform":"qq","type":"group"}}
{"_type":"member","platformId":"123456","accountName":"John","groupNickname":"Owner","roles":[{"id":"owner"}]}
{"_type":"member","platformId":"789012","accountName":"Alice"}
{"_type":"message","platformMessageId":"msg_001","sender":"123456","accountName":"John","groupNickname":"Owner","timestamp":1703001600,"type":0,"content":"Hello everyone!"}
{"_type":"message","sender":"789012","accountName":"Alice","timestamp":1703001610,"type":0,"content":"Hi!"}
{"_type":"message","sender":"123456","accountName":"John","groupNickname":"Owner","timestamp":1703001620,"type":1,"content":"[Image]"}
```

### Parsing Rules

1. **The first line must be `header`**: it contains the `chatlab` version and `meta` information
2. **Member lines come before message lines**: optional, and if omitted, member information can be collected from messages automatically
3. **Messages should be sorted chronologically**: it is recommended to sort by `timestamp` in ascending order
4. **Each line must be self-contained**: a parsing error on one line can be skipped without stopping the whole process
5. **Comment lines are supported**: lines starting with `#` will be skipped and can be used for notes

::: warning Note

- Every line must be **valid JSON** and cannot span multiple lines
- Lines are separated by the newline character `\n`

:::

## Version History

| Version | Date       | Changes                                                                 |
| ------- | ---------- | ----------------------------------------------------------------------- |
| 0.0.1   | 2025-12-22 | Initial version                                                         |
| 0.0.2   | 2026-01-09 | Added `roles`, `ownerId`, `platformMessageId`, and `replyToMessageId`; added JSONL format |
