Skip to main content

nagisa_types/
id.rs

1//! 寻址基元:QQ 号 [`Uin`]、会话场景 [`Scene`]、对端寻址 [`Peer`]、不透明消息 ID
2//! [`MessageId`]。事件、消息段、实体都靠这几个类型互相引用。
3use serde::{Deserialize, Serialize};
4use std::fmt;
5
6/// QQ 号。用 newtype 包住 i64,防止与普通整数混淆。serde 透明序列化为内层 i64。
7#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default, Serialize, Deserialize)]
8pub struct Uin(pub i64);
9
10/// 会话场景。
11#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)]
12#[serde(rename_all = "snake_case")]
13pub enum Scene {
14    Friend,
15    Group,
16    Temp,
17}
18
19/// 会话寻址:场景 + 对端 id(好友 QQ 号或群号)。
20#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)]
21pub struct Peer {
22    pub scene: Scene,
23    pub id: Uin,
24}
25
26/// 不透明消息标识:打包两协议各自的定位信息。业务把它当不透明句柄,不对其做算术/比较。
27/// - Milky 用 `(peer.scene, peer.id, seq)` 三元组。
28/// - OneBot 用 `onebot_id` 合成整数。
29///
30/// **固有限制(无 time 槽)**:不携带发送时间。Milky 的发送响应同时回传 `message_seq` 与
31/// `time`,但本类型没有 time 字段,故发送返回路径有意丢弃 `time`(见 `nagisa-milky/src/actions.rs`
32/// 的 `send`)。下游若需时间戳,应经 `get_message(id).time` 回查——这是统一类型不带时间槽的
33/// 固有取舍,并非解码缺陷。
34///
35/// 派生 serde 以便持久化/缓存(OneBot 适配器的 message_id 映射缓存会用到)。
36#[derive(Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)]
37pub struct MessageId {
38    pub peer: Peer,
39    pub seq: i64,
40    pub onebot_id: Option<i32>,
41}
42
43impl From<i64> for Uin {
44    fn from(v: i64) -> Self {
45        Uin(v)
46    }
47}
48
49impl Uin {
50    /// 是否像一个真实用户 QQ 号——启发式(≥ 10000),非类型保证。系统号/匿名等可能落在
51    /// 此范围外。供「@-或-QQ 号」入参等场景做尽力校验,省得各处手写 `< 10000` 魔法数;
52    /// 需要权威判断时仍应结合具体协议语境。
53    pub fn is_user(self) -> bool {
54        self.0 >= 10_000
55    }
56}
57
58impl fmt::Display for Uin {
59    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60        write!(f, "{}", self.0)
61    }
62}
63
64impl Peer {
65    pub fn group(id: impl Into<Uin>) -> Self {
66        Peer { scene: Scene::Group, id: id.into() }
67    }
68    pub fn friend(id: impl Into<Uin>) -> Self {
69        Peer { scene: Scene::Friend, id: id.into() }
70    }
71    pub fn temp(id: impl Into<Uin>) -> Self {
72        Peer { scene: Scene::Temp, id: id.into() }
73    }
74    pub fn is_group(&self) -> bool {
75        matches!(self.scene, Scene::Group)
76    }
77}
78
79impl MessageId {
80    /// Milky 风格:由 (peer, seq) 构造。
81    pub fn from_seq(peer: Peer, seq: i64) -> Self {
82        MessageId { peer, seq, onebot_id: None }
83    }
84}