Metadata-Version: 2.4
Name: nonebot-plugin-uniref
Version: 0.2.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-uninfo>=0.11.1,<1.0.0
Requires-Dist: nonebot2>=2.5.0,<3.0.0
Requires-Dist: nonebot-plugin-alconna>=0.62.1,<1.0.0 ; extra == 'alconna'
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
Provides-Extra: alconna
Description-Content-Type: text/markdown

# nonebot-plugin-uniref

`nonebot-plugin-uniref`（UniRef）为 NoneBot 提供可持久化、可比较、可序列化的跨平台用户与场景实体引用。

> 当前状态：v0.1 已完成本地实现和测试，尚未发布。

v0.1 只处理两类实体：

- `UserRef(scope, id)`：平台或实例身份空间中的用户；
- `SceneRef(scope, type, id)`：平台或实例身份空间中的场景；`id` 是完整规范标识，不保证与 Uninfo 原始
  `scene.id` 相同。

它们不绑定某个 Bot，不表示资料当前可查询，也不负责业务数据分区或公共数据库 Registry。安装可选的
Alconna 集成后，可以从可发送的 Ref 确定性构造消息目标；实际发送仍取决于运行时 Bot、权限和平台状态。

公开 API 是：

```python
UserRef
SceneRef
EventUserRef
EventSceneRef
get_user_ref
get_scene_ref
encode_ref
decode_ref
```

可选的 Alconna 集成另外公开：

```python
TargetUnavailableError
to_target
```

其中 `EventUserRef` 与 `EventSceneRef` 是 NoneBot 依赖注入别名；基础函数仍可直接调用。发行包名使用
`nonebot-plugin-uniref`，Python 导入包名使用 `nonebot_plugin_uniref`。

## 使用

事件处理函数可以直接声明当前用户或场景：

```python
from nonebot_plugin_uniref import EventSceneRef, EventUserRef


@matcher.handle()
async def handle(user: EventUserRef, scene: EventSceneRef) -> None: ...
```

业务插件可以把 Ref 编码成一个规范字符串，之后再恢复原类型：

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

ref = UserRef(scope="QQClient", id="123")
value = encode_ref(ref)
assert value == "uniref:v1:user:QQClient:123"
assert decode_ref(value) == ref
```

需要从 Ref 恢复主动发送目标时，安装 Alconna extra：

```console
uv add "nonebot-plugin-uniref[alconna]"
```

然后可以直接转换；传入 `bot` 时会验证 Adapter 并把 Target 绑定到该 Bot：

```python
from nonebot_plugin_alconna import UniMessage
from nonebot_plugin_uniref import decode_ref, to_target

ref = decode_ref("uniref:v1:scene:QQClient:group:456")
target = to_target(ref, bot=bot)
await UniMessage("hello").send(target=target, bot=bot)
```

`to_target()` 不查询平台，也不保证目标当前可达。有效 Ref 如果不是可发送实体，例如 Discord Guild，或者
当前集成尚未验证相应 scope/场景类型，会抛出 `TargetUnavailableError`。

`get_user_ref` / `get_scene_ref` 只从已验证的 Uninfo Adapter/scope 组合自动提取。v0.1 首批支持
OneBot V11 × QQClient、原生 Telegram 和原生 Discord；其他来源即使使用相同 scope 也会显式失败。直接构造
Ref 和使用 codec 不受这个提取白名单限制。完整范围见
[平台提取与目标转换支持矩阵](docs/architecture/platform-support.md)。

Uninfo 负责提供统一的当前事件事实，UniRef 再判断这些事实是否足以形成可长期保存的身份。场景 parent
仍是拓扑资料，不进入 `SceneRef`；若裸场景 ID 只在 parent 内唯一，提取层必须生成完整 ID 或显式失败。
v0.1 尚未定义 Telegram topic 的复合 ID 格式，因此暂不为 Telegram topic 生成 `SceneRef`。

## 本地开发

```console
just sync
just hooks
just lint
just check
just test
uv build
```

仓库基于 `nonebot-plugin-template` 的 Copier `v0.2.0` 基线维护。模板升级前需要保持工作树干净，然后运行：

```console
just update-template
```

`just bump` 只允许在 `main` 分支执行，会创建版本提交和 annotated tag，并将提交与标签原子推送到
`origin`。首次发布前还需要在 GitHub 的 `release` environment 和 PyPI 中配置 Trusted Publishing。

## 从哪里开始

1. [项目边界与公开契约](docs/architecture/overview.md)
2. [ADR-0001：v0.1 采用最小实体 Ref 协议](docs/adr/0001-adopt-minimal-entity-ref-protocol.md)
3. [ADR-0002：由 UniRef 从 Uninfo 事实生成持久化身份](docs/adr/0002-generate-persistent-identity-from-uninfo-facts.md)
4. [ADR-0003：v0.1 只从已验证的 Uninfo 来源提取 Ref](docs/adr/0003-only-extract-from-verified-uninfo-sources.md)
5. [ADR-0004：通过可选 Alconna 集成从 Ref 派生发送目标](docs/adr/0004-derive-alconna-targets-from-refs.md)
6. [平台提取与目标转换支持矩阵](docs/architecture/platform-support.md)
7. [现有设计基线](docs/scratch/identity-design-baseline.md)
8. [尚待落实的协议细节](docs/scratch/open-questions.md)
9. [来源清单](docs/scratch/source-inventory.md)

完整文档导航见 [docs/README.md](docs/README.md)。
