Skip to main content

wechat_profile/
model.rs

1use serde::{Deserialize, Serialize};
2use std::collections::BTreeMap;
3
4/// Local IDs are scoped to a database *and* a table. Decimal strings avoid a
5/// lossy JSON floating-point conversion and are not native server checkpoints.
6#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
7#[serde(rename_all = "camelCase", deny_unknown_fields)]
8pub struct SourceMessageId {
9    pub database: String,
10    pub table: String,
11    pub local_id: String,
12}
13
14#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
15#[serde(tag = "storageType", content = "value", rename_all = "camelCase")]
16pub enum SqlValue {
17    Null,
18    Integer(String),
19    // Strings preserve non-finite SQLite doubles, which JSON numbers cannot
20    // represent, as well as finite round-trip values and negative zero.
21    Real(String),
22    Text(String),
23    TextBytes(String),
24    Blob(String),
25}
26pub type RawFields = BTreeMap<String, SqlValue>;
27
28#[derive(Clone, Debug, Serialize)]
29#[serde(rename_all = "camelCase")]
30pub struct DecodedField {
31    pub text: Option<String>,
32    pub state: DecodeState,
33    pub compression_flag: Option<i64>,
34}
35
36#[derive(Clone, Copy, Debug, Serialize, PartialEq, Eq)]
37#[serde(rename_all = "snake_case")]
38pub enum DecodeState {
39    Null,
40    Utf8,
41    Zstd,
42    InvalidUtf8,
43    UnsupportedCompression,
44    InvalidCompressedData,
45}
46
47#[derive(Clone, Debug, Serialize)]
48#[serde(rename_all = "camelCase")]
49pub struct Message {
50    pub id: SourceMessageId,
51    pub server_message_id: Option<String>,
52    pub server_id_signed: Option<String>,
53    pub peer: Option<String>,
54    pub sender: Option<String>,
55    pub outgoing: Option<bool>,
56    pub message_type: i64,
57    pub local_type: String,
58    pub create_time: i64,
59    pub sort_seq: Option<String>,
60    pub content: DecodedField,
61    pub source: DecodedField,
62    pub raw_fields: RawFields,
63}
64
65/// Membership for a history view, without reading or decoding message bodies.
66#[derive(Clone, Debug)]
67pub struct MessageReference {
68    pub id: SourceMessageId,
69    pub peer: Option<String>,
70}
71
72#[derive(Clone, Debug, Serialize)]
73#[serde(rename_all = "camelCase")]
74pub struct Contact {
75    pub username: String,
76    pub local_id: String,
77    pub label: String,
78    pub nickname: Option<String>,
79    pub remark: Option<String>,
80    pub alias: Option<String>,
81    pub local_type: Option<i64>,
82    pub is_group: bool,
83    pub raw_fields: RawFields,
84}
85
86#[derive(Clone, Debug, Serialize)]
87#[serde(rename_all = "camelCase")]
88pub struct Thread {
89    pub username: String,
90    pub label: String,
91    pub unread_count: Option<i64>,
92    pub hidden: Option<bool>,
93    pub sort_timestamp: Option<i64>,
94    pub last_timestamp: Option<i64>,
95    pub raw_fields: RawFields,
96}
97
98#[derive(Clone, Debug, Serialize)]
99#[serde(rename_all = "camelCase")]
100pub struct Group {
101    pub username: String,
102    pub local_id: String,
103    pub label: String,
104    pub owner: Option<String>,
105    pub announcement: Option<String>,
106    pub raw_fields: RawFields,
107}
108
109#[derive(Clone, Debug, Serialize)]
110#[serde(rename_all = "camelCase")]
111pub struct GroupMember {
112    pub username: String,
113    pub local_id: String,
114    pub label: String,
115    pub room_display_name: Option<String>,
116    pub contact: Contact,
117}
118
119/// Derived from the official room owner, administrator list and SQL membership.
120/// Missing profile evidence stays unknown; no separate permission state is saved.
121#[derive(Clone, Debug)]
122pub struct GroupMentionPermissions {
123    pub administrator_ids: Option<Vec<String>>,
124    pub can_mention_all: Option<bool>,
125}
126
127#[derive(Clone, Debug, Serialize)]
128#[serde(rename_all = "camelCase")]
129pub struct CachedBytes {
130    pub media_type: String,
131    pub sha256: String,
132    pub bytes: Vec<u8>,
133    pub source_database: String,
134    pub source_table: String,
135}
136
137#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)]
138#[serde(rename_all = "snake_case")]
139pub enum ImageQuality {
140    Best,
141    Original,
142    Standard,
143    Thumbnail,
144}
145
146#[derive(Clone, Debug, Serialize)]
147#[serde(rename_all = "camelCase")]
148pub struct CachedImage {
149    pub media: CachedBytes,
150    /// Original requires advertised content MD5. Standard/Thumbnail require an
151    /// exact native resource join; a filename suffix alone remains Unknown.
152    pub quality: VerifiedImageQuality,
153    pub width: u32,
154    pub height: u32,
155    pub cache_variant: String,
156}
157#[derive(Clone, Copy, Debug, Serialize, PartialEq, Eq)]
158#[serde(rename_all = "snake_case")]
159pub enum VerifiedImageQuality {
160    Original,
161    Standard,
162    Thumbnail,
163    Unknown,
164}
165
166#[derive(Clone, Debug, Serialize)]
167#[serde(rename_all = "camelCase")]
168pub struct DatabaseInventory {
169    pub relative_path: String,
170    pub bytes: u64,
171    pub plaintext: bool,
172    pub has_wal: bool,
173    pub generation: String,
174    pub state: DatabaseState,
175}
176
177#[derive(Clone, Debug, Serialize, PartialEq, Eq)]
178#[serde(rename_all = "snake_case")]
179pub enum DatabaseState {
180    Readable,
181    KeyUnavailable,
182    KeyMismatch,
183    UnsupportedSchema,
184}
185
186#[derive(Clone, Debug, Serialize)]
187#[serde(rename_all = "camelCase")]
188pub struct Coverage {
189    pub partial: bool,
190    pub databases: Vec<DatabaseInventory>,
191}
192
193#[derive(Clone, Debug, Serialize)]
194#[serde(rename_all = "camelCase")]
195pub struct Page<T> {
196    pub items: Vec<T>,
197    pub next_cursor: Option<String>,
198    pub source_generation: String,
199    pub coverage: Coverage,
200}
201
202#[derive(Clone, Debug, Default)]
203pub struct ListQuery {
204    pub limit: Option<usize>,
205    pub cursor: Option<String>,
206    pub search: Option<String>,
207}
208
209#[derive(Clone, Debug, Default)]
210pub struct MessageQuery {
211    pub chat: Option<String>,
212    pub own_username: Option<String>,
213    pub limit: Option<usize>,
214    pub cursor: Option<String>,
215    pub message_type: Option<i64>,
216}