Metadata-Version: 2.4
Name: nonebot-plugin-uniref
Version: 0.4.0
Summary: Portable and serializable entity references for NoneBot
Author: Misty02600
Author-email: Misty02600 <xiao02600@gmail.com>
License-Expression: MIT
License-File: LICENSE
Requires-Dist: nonebot-plugin-alconna>=0.62.1,<0.63.0
Requires-Dist: nonebot-plugin-uninfo>=0.11.1,<0.12.0
Requires-Dist: nonebot2>=2.5.0,<3.0.0
Requires-Python: >=3.11
Project-URL: Homepage, https://github.com/Misty02600/nonebot-plugin-uniref
Project-URL: Issues, https://github.com/Misty02600/nonebot-plugin-uniref/issues
Project-URL: Repository, https://github.com/Misty02600/nonebot-plugin-uniref.git
Description-Content-Type: text/markdown

<div align="center">

  <a href="https://nonebot.dev/">
    <img src="https://nonebot.dev/logo.png" width="200" height="200" alt="nonebot">
  </a>

# nonebot-plugin-uniref

_✨ [NoneBot2](https://github.com/nonebot/nonebot2) 可持久化的跨平台实体引用插件 ✨_

<p align="center">
  <img src="https://img.shields.io/github/license/Misty02600/nonebot-plugin-uniref" alt="license">
  <img src="https://img.shields.io/badge/python-3.11+-blue.svg" alt="Python">
  <img src="https://img.shields.io/badge/nonebot-2.5.0+-red.svg" alt="NoneBot">
  <a href="https://pypi.org/project/nonebot-plugin-uniref">
    <img src="https://badgen.net/pypi/v/nonebot-plugin-uniref" alt="pypi">
  </a>
</p>

</div>

本插件提供可比较、可序列化的 `UserRef` 与 `SceneRef`，用于在 NoneBot 插件中持久化用户和场景身份。

## 安装

- 使用 nb-cli

```console
nb plugin install nonebot-plugin-uniref
```

- 使用 pip

```console
pip install nonebot-plugin-uniref
```

## 使用

### 从事件上下文获取和构造 Ref

```python
from nonebot_plugin_alconna import At, UniMsg
from nonebot_plugin_uniref import RefContext


@matcher.handle()
async def handle(message: UniMsg, refs: RefContext):
    user = refs.user_ref
    scene = refs.scene_ref
    user_ids = [at.target for at in message[At] if at.flag == "user"]
    users = [refs.build_user_ref(user_id) for user_id in user_ids]
    data_scene = refs.parent_scene_ref or scene
```

### 编码与恢复 Ref

```python
from nonebot_plugin_uniref import UserRef, decode_ref, encode_ref

ref = UserRef(namespace="QQClient", id="123")
value = encode_ref(ref)

assert value == "user:QQClient:123"
assert decode_ref(value) == ref
```

### 构造 Alconna Target

```python
from nonebot_plugin_uniref import decode_ref, ref_to_target

ref = decode_ref("scene:QQClient:group:456")
target = ref_to_target(ref, bot=bot)
```

## 模型定义

### `UserRef`

| 属性        | 类型 | 含义                       |
| ----------- | ---- | -------------------------- |
| `namespace` | str  | 用户 ID 所属的身份命名空间 |
| `id`        | str  | 用户 ID                    |

### `SceneRef`

| 属性        | 类型        | 含义                       |
| ----------- | ----------- | -------------------------- |
| `namespace` | str         | 场景 ID 所属的身份命名空间 |
| `type`      | `SceneKind` | UniRef 定义的场景种类      |
| `id`        | str         | 场景 ID                    |

`SceneKind` 包含 `private`、`group`、`guild`、`channel_text`、`channel_category` 和 `channel_voice`。


## 支持的 Adapter

`{...}` 表示决定 ID 相等范围的平台值；`id=` 后才是实际的 `Ref.id`。Scene 类型已经由 `SceneRef.type`
保存，所以 namespace 不再重复 `private`、`group` 或 `channel`。

| 已验证 Adapter / 场景 | UserRef：namespace；ID                                        | SceneRef：namespace；type；ID                                                          |
| --------------------- | ------------------------------------------------------------- | -------------------------------------------------------------------------------------- |
| OneBot V11、Milky     | `QQClient`；id=QQ 号                                          | `QQClient`；`private`：QQ 号；`group`：群号                                            |
| Telegram              | `Telegram`；id=User ID                                        | `Telegram`；`private`、`group`：id=Chat ID；`channel_text`：id=`{Chat ID}:{Thread ID}` |
| Discord               | `Discord`；id=User Snowflake                                  | `Discord`；`private`、`guild`、Channel；id=Snowflake                                   |
| QQ C2C                | `QQAPI:{QQ AppID}:c2c`；id=`user_openid`                      | `QQAPI:{QQ AppID}`；`private`；id=`user_openid`                                        |
| QQ 群聊               | `QQAPI:{QQ AppID}:group`；id=`{group_openid}:{member_openid}` | `QQAPI:{QQ AppID}`；`group`；id=`group_openid`                                         |
| QQ 公开文字子频道     | `QQAPI:{QQ AppID}:guild`；id=`{guild_id}:{author.id}`         | `QQAPI:{QQ AppID}`；`channel_text`；id=`channel_id`                                    |
| 飞书私聊              | `Feishu:{Feishu AppID}`；id=`open_id`                         | 同 namespace；`private`；id=`open_id`                                                  |
| 飞书群聊              | `Feishu:{Feishu AppID}`；id=`open_id`                         | `Feishu`；`group`；id=`chat_id`                                                        |
