1use serde::{Deserialize, Serialize};
25
26#[derive(Debug, Clone, Serialize, Deserialize, Default)]
35pub struct SessionReadyRequest {}
36
37pub type SessionReadyResponse = bool;
41
42#[derive(Debug, Clone, Serialize, Deserialize)]
50pub struct ClientSubmitRequest {
51 pub local_message_id: u64,
53
54 pub channel_id: u64,
56
57 pub channel_type: u8,
59
60 pub last_pts: u64,
62
63 pub command_type: String,
65
66 pub payload: serde_json::Value,
68
69 pub client_timestamp: i64,
71
72 #[serde(skip_serializing_if = "Option::is_none")]
74 pub device_id: Option<String>,
75}
76
77#[derive(Debug, Clone, Serialize, Deserialize)]
82pub struct ClientSubmitResponse {
83 pub decision: ServerDecision,
85
86 #[serde(skip_serializing_if = "Option::is_none")]
88 pub pts: Option<u64>,
89
90 #[serde(skip_serializing_if = "Option::is_none")]
92 pub server_msg_id: Option<u64>,
93
94 pub server_timestamp: i64,
96
97 pub local_message_id: u64,
99
100 pub has_gap: bool,
102
103 pub current_pts: u64,
105}
106
107#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
109#[serde(rename_all = "snake_case")]
110pub enum ServerDecision {
111 Accepted,
113 Transformed {
115 reason: String,
117 },
118 Rejected {
120 reason: String,
122 },
123}
124
125#[derive(Debug, Clone, Serialize, Deserialize)]
133pub struct GetDifferenceRequest {
134 pub channel_id: u64,
136
137 pub channel_type: u8,
139
140 pub last_pts: u64,
142
143 #[serde(skip_serializing_if = "Option::is_none")]
145 pub limit: Option<u32>,
146}
147
148#[derive(Debug, Clone, Serialize, Deserialize)]
152pub struct GetDifferenceResponse {
153 pub commits: Vec<ServerCommit>,
155
156 pub current_pts: u64,
158
159 pub has_more: bool,
161}
162
163#[derive(Debug, Clone, Serialize, Deserialize)]
165pub struct ServerCommit {
166 pub pts: u64,
168
169 pub server_msg_id: u64,
171
172 #[serde(skip_serializing_if = "Option::is_none")]
174 pub local_message_id: Option<u64>,
175
176 pub channel_id: u64,
178
179 pub channel_type: u8,
181
182 pub message_type: String,
184
185 pub content: serde_json::Value,
187
188 pub server_timestamp: i64,
190
191 pub sender_id: u64,
193
194 #[serde(skip_serializing_if = "Option::is_none")]
196 pub sender_info: Option<SenderInfo>,
197}
198
199#[derive(Debug, Clone, Serialize, Deserialize)]
201pub struct SenderInfo {
202 pub user_id: u64,
203 pub username: String,
204 #[serde(skip_serializing_if = "Option::is_none")]
205 pub nickname: Option<String>,
206 #[serde(skip_serializing_if = "Option::is_none")]
207 pub avatar_url: Option<String>,
208}
209
210#[derive(Debug, Clone, Serialize, Deserialize)]
218pub struct GetChannelPtsRequest {
219 pub channel_id: u64,
221
222 pub channel_type: u8,
224}
225
226#[derive(Debug, Clone, Serialize, Deserialize)]
230pub struct GetChannelPtsResponse {
231 pub current_pts: u64,
233}
234
235#[derive(Debug, Clone, Serialize, Deserialize)]
243pub struct BatchGetChannelPtsRequest {
244 pub channels: Vec<ChannelIdentifier>,
246}
247
248#[derive(Debug, Clone, Serialize, Deserialize)]
250pub struct ChannelIdentifier {
251 pub channel_id: u64,
252 pub channel_type: u8,
253}
254
255#[derive(Debug, Clone, Serialize, Deserialize)]
259pub struct BatchGetChannelPtsResponse {
260 pub channel_pts_map: Vec<ChannelPtsInfo>,
262}
263
264#[derive(Debug, Clone, Serialize, Deserialize)]
266pub struct ChannelPtsInfo {
267 pub channel_id: u64,
268 pub channel_type: u8,
269 pub current_pts: u64,
270}
271
272#[derive(Debug, Clone, Serialize, Deserialize)]
281pub struct SyncEntitiesRequest {
282 pub entity_type: String,
284 #[serde(skip_serializing_if = "Option::is_none")]
286 pub since_version: Option<u64>,
287 #[serde(skip_serializing_if = "Option::is_none")]
289 pub scope: Option<String>,
290 #[serde(skip_serializing_if = "Option::is_none")]
292 pub limit: Option<u32>,
293}
294
295#[derive(Debug, Clone, Serialize, Deserialize)]
297pub struct SyncEntityItem {
298 pub entity_id: String,
300 pub version: u64,
302 #[serde(default)]
304 pub deleted: bool,
305 #[serde(skip_serializing_if = "Option::is_none")]
307 pub payload: Option<serde_json::Value>,
308}
309
310#[derive(Debug, Clone, Default, Serialize, Deserialize)]
311pub struct FriendSyncFriendPayload {
312 #[serde(skip_serializing_if = "Option::is_none")]
313 pub created_at: Option<i64>,
314 #[serde(skip_serializing_if = "Option::is_none")]
315 pub updated_at: Option<i64>,
316 #[serde(skip_serializing_if = "Option::is_none")]
317 pub version: Option<i64>,
318}
319
320#[derive(Debug, Clone, Default, Serialize, Deserialize)]
321pub struct FriendSyncUserPayload {
322 #[serde(skip_serializing_if = "Option::is_none")]
323 pub username: Option<String>,
324 #[serde(skip_serializing_if = "Option::is_none")]
325 pub nickname: Option<String>,
326 #[serde(skip_serializing_if = "Option::is_none")]
327 pub name: Option<String>,
328 #[serde(skip_serializing_if = "Option::is_none")]
329 pub alias: Option<String>,
330 #[serde(skip_serializing_if = "Option::is_none")]
331 pub avatar: Option<String>,
332 #[serde(skip_serializing_if = "Option::is_none")]
333 pub user_type: Option<i32>,
334 #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
335 pub type_field: Option<i32>,
336 #[serde(skip_serializing_if = "Option::is_none")]
337 pub updated_at: Option<i64>,
338 #[serde(skip_serializing_if = "Option::is_none")]
339 pub version: Option<i64>,
340}
341
342#[derive(Debug, Clone, Default, Serialize, Deserialize)]
343pub struct FriendSyncPayload {
344 #[serde(skip_serializing_if = "Option::is_none")]
345 pub user_id: Option<u64>,
346 #[serde(skip_serializing_if = "Option::is_none")]
347 pub uid: Option<u64>,
348 #[serde(skip_serializing_if = "Option::is_none")]
349 pub tags: Option<String>,
350 #[serde(skip_serializing_if = "Option::is_none")]
351 pub is_pinned: Option<bool>,
352 #[serde(skip_serializing_if = "Option::is_none")]
353 pub pinned: Option<bool>,
354 #[serde(skip_serializing_if = "Option::is_none")]
355 pub created_at: Option<i64>,
356 #[serde(skip_serializing_if = "Option::is_none")]
357 pub updated_at: Option<i64>,
358 #[serde(skip_serializing_if = "Option::is_none")]
359 pub version: Option<i64>,
360 #[serde(skip_serializing_if = "Option::is_none")]
361 pub friend: Option<FriendSyncFriendPayload>,
362 #[serde(skip_serializing_if = "Option::is_none")]
363 pub user: Option<FriendSyncUserPayload>,
364}
365
366#[derive(Debug, Clone, Default, Serialize, Deserialize)]
367pub struct GroupSyncPayload {
368 #[serde(skip_serializing_if = "Option::is_none")]
369 pub group_id: Option<u64>,
370 #[serde(skip_serializing_if = "Option::is_none")]
371 pub name: Option<String>,
372 #[serde(skip_serializing_if = "Option::is_none")]
373 pub description: Option<String>,
374 #[serde(skip_serializing_if = "Option::is_none")]
375 pub avatar: Option<String>,
376 #[serde(skip_serializing_if = "Option::is_none")]
377 pub avatar_url: Option<String>,
378 #[serde(skip_serializing_if = "Option::is_none")]
379 pub owner_id: Option<u64>,
380 #[serde(skip_serializing_if = "Option::is_none")]
381 pub member_count: Option<u32>,
382 #[serde(skip_serializing_if = "Option::is_none")]
383 pub created_at: Option<i64>,
384 #[serde(skip_serializing_if = "Option::is_none")]
385 pub updated_at: Option<i64>,
386}
387
388#[derive(Debug, Clone, Default, Serialize, Deserialize)]
389pub struct ChannelSyncPayload {
390 #[serde(skip_serializing_if = "Option::is_none")]
391 pub channel_id: Option<u64>,
392 #[serde(skip_serializing_if = "Option::is_none")]
393 pub channel_type: Option<i64>,
394 #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
395 pub type_field: Option<i64>,
396 #[serde(skip_serializing_if = "Option::is_none")]
397 pub channel_name: Option<String>,
398 #[serde(skip_serializing_if = "Option::is_none")]
399 pub name: Option<String>,
400 #[serde(skip_serializing_if = "Option::is_none")]
401 pub avatar: Option<String>,
402 #[serde(skip_serializing_if = "Option::is_none")]
403 pub unread_count: Option<i32>,
404 #[serde(skip_serializing_if = "Option::is_none")]
405 pub last_msg_content: Option<String>,
406 #[serde(skip_serializing_if = "Option::is_none")]
407 pub last_msg_timestamp: Option<i64>,
408 #[serde(skip_serializing_if = "Option::is_none")]
409 pub top: Option<i32>,
410 #[serde(skip_serializing_if = "Option::is_none")]
411 pub mute: Option<i32>,
412}
413
414#[derive(Debug, Clone, Default, Serialize, Deserialize)]
415pub struct ChannelExtraSyncPayload {
416 #[serde(skip_serializing_if = "Option::is_none")]
417 pub channel_id: Option<u64>,
418 #[serde(skip_serializing_if = "Option::is_none")]
419 pub channel_type: Option<i64>,
420 #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
421 pub type_field: Option<i64>,
422 #[serde(skip_serializing_if = "Option::is_none")]
423 pub browse_to: Option<u64>,
424 #[serde(skip_serializing_if = "Option::is_none")]
425 pub keep_pts: Option<u64>,
426 #[serde(skip_serializing_if = "Option::is_none")]
427 pub keep_offset_y: Option<i32>,
428 #[serde(skip_serializing_if = "Option::is_none")]
429 pub draft: Option<String>,
430 #[serde(skip_serializing_if = "Option::is_none")]
431 pub draft_updated_at: Option<u64>,
432}
433
434#[derive(Debug, Clone, Default, Serialize, Deserialize)]
435pub struct ChannelUnreadSyncPayload {
436 #[serde(skip_serializing_if = "Option::is_none")]
437 pub channel_id: Option<u64>,
438 #[serde(skip_serializing_if = "Option::is_none")]
439 pub channel_type: Option<i64>,
440 #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
441 pub type_field: Option<i64>,
442 #[serde(skip_serializing_if = "Option::is_none")]
443 pub unread_count: Option<i32>,
444}
445
446#[derive(Debug, Clone, Default, Serialize, Deserialize)]
447pub struct UserSettingsSyncPayload {
448 #[serde(skip_serializing_if = "Option::is_none")]
449 pub setting_key: Option<String>,
450 #[serde(skip_serializing_if = "Option::is_none")]
451 pub value: Option<serde_json::Value>,
452}
453
454#[derive(Debug, Clone, Default, Serialize, Deserialize)]
455pub struct GroupMemberSyncPayload {
456 #[serde(skip_serializing_if = "Option::is_none")]
457 pub group_id: Option<u64>,
458 #[serde(skip_serializing_if = "Option::is_none")]
459 pub user_id: Option<u64>,
460 #[serde(skip_serializing_if = "Option::is_none")]
461 pub uid: Option<u64>,
462 #[serde(skip_serializing_if = "Option::is_none")]
463 pub role: Option<i32>,
464 #[serde(skip_serializing_if = "Option::is_none")]
465 pub status: Option<i32>,
466 #[serde(skip_serializing_if = "Option::is_none")]
467 pub alias: Option<String>,
468 #[serde(skip_serializing_if = "Option::is_none")]
469 pub is_muted: Option<bool>,
470 #[serde(skip_serializing_if = "Option::is_none")]
471 pub joined_at: Option<i64>,
472 #[serde(skip_serializing_if = "Option::is_none")]
473 pub updated_at: Option<i64>,
474 #[serde(skip_serializing_if = "Option::is_none")]
475 pub version: Option<i64>,
476}
477
478#[derive(Debug, Clone, Default, Serialize, Deserialize)]
479pub struct ChannelMemberSyncPayload {
480 #[serde(skip_serializing_if = "Option::is_none")]
481 pub channel_id: Option<u64>,
482 #[serde(skip_serializing_if = "Option::is_none")]
483 pub channel_type: Option<i32>,
484 #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
485 pub type_field: Option<i32>,
486 #[serde(skip_serializing_if = "Option::is_none")]
487 pub member_uid: Option<u64>,
488 #[serde(skip_serializing_if = "Option::is_none")]
489 pub user_id: Option<u64>,
490 #[serde(skip_serializing_if = "Option::is_none")]
491 pub uid: Option<u64>,
492 #[serde(skip_serializing_if = "Option::is_none")]
493 pub member_name: Option<String>,
494 #[serde(skip_serializing_if = "Option::is_none")]
495 pub name: Option<String>,
496 #[serde(skip_serializing_if = "Option::is_none")]
497 pub member_remark: Option<String>,
498 #[serde(skip_serializing_if = "Option::is_none")]
499 pub remark: Option<String>,
500 #[serde(skip_serializing_if = "Option::is_none")]
501 pub member_avatar: Option<String>,
502 #[serde(skip_serializing_if = "Option::is_none")]
503 pub avatar: Option<String>,
504 #[serde(skip_serializing_if = "Option::is_none")]
505 pub member_invite_uid: Option<u64>,
506 #[serde(skip_serializing_if = "Option::is_none")]
507 pub inviter_uid: Option<u64>,
508 #[serde(skip_serializing_if = "Option::is_none")]
509 pub role: Option<i32>,
510 #[serde(skip_serializing_if = "Option::is_none")]
511 pub status: Option<i32>,
512 #[serde(skip_serializing_if = "Option::is_none")]
513 pub is_deleted: Option<bool>,
514 #[serde(skip_serializing_if = "Option::is_none")]
515 pub robot: Option<i32>,
516 #[serde(skip_serializing_if = "Option::is_none")]
517 pub version: Option<i64>,
518 #[serde(skip_serializing_if = "Option::is_none")]
519 pub created_at: Option<i64>,
520 #[serde(skip_serializing_if = "Option::is_none")]
521 pub updated_at: Option<i64>,
522 #[serde(skip_serializing_if = "Option::is_none")]
523 pub extra: Option<String>,
524 #[serde(skip_serializing_if = "Option::is_none")]
525 pub forbidden_expiration_time: Option<i64>,
526 #[serde(skip_serializing_if = "Option::is_none")]
527 pub member_avatar_cache_key: Option<String>,
528}
529
530#[derive(Debug, Clone, Default, Serialize, Deserialize)]
531pub struct MessageSyncPayload {
532 #[serde(skip_serializing_if = "Option::is_none")]
533 pub server_message_id: Option<u64>,
534 #[serde(skip_serializing_if = "Option::is_none")]
535 pub message_id: Option<u64>,
536 #[serde(skip_serializing_if = "Option::is_none")]
537 pub id: Option<u64>,
538 #[serde(skip_serializing_if = "Option::is_none")]
539 pub local_message_id: Option<u64>,
540 #[serde(skip_serializing_if = "Option::is_none")]
541 pub channel_id: Option<u64>,
542 #[serde(skip_serializing_if = "Option::is_none")]
543 pub channel_type: Option<i32>,
544 #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
545 pub type_field: Option<i32>,
546 #[serde(skip_serializing_if = "Option::is_none")]
547 pub conversation_type: Option<i32>,
548 #[serde(skip_serializing_if = "Option::is_none")]
549 pub timestamp: Option<i64>,
550 #[serde(skip_serializing_if = "Option::is_none")]
551 pub created_at: Option<i64>,
552 #[serde(skip_serializing_if = "Option::is_none")]
553 pub send_time: Option<i64>,
554 #[serde(skip_serializing_if = "Option::is_none")]
555 pub from_uid: Option<u64>,
556 #[serde(skip_serializing_if = "Option::is_none")]
557 pub sender_id: Option<u64>,
558 #[serde(skip_serializing_if = "Option::is_none")]
559 pub from: Option<u64>,
560 #[serde(skip_serializing_if = "Option::is_none")]
561 pub uid: Option<u64>,
562 #[serde(skip_serializing_if = "Option::is_none")]
563 pub message_type: Option<i32>,
564 #[serde(skip_serializing_if = "Option::is_none")]
565 pub content: Option<String>,
566 #[serde(skip_serializing_if = "Option::is_none")]
567 pub text: Option<String>,
568 #[serde(skip_serializing_if = "Option::is_none")]
569 pub body: Option<String>,
570 #[serde(skip_serializing_if = "Option::is_none")]
571 pub status: Option<i32>,
572 #[serde(skip_serializing_if = "Option::is_none")]
573 pub pts: Option<i64>,
574 #[serde(skip_serializing_if = "Option::is_none")]
575 pub setting: Option<i32>,
576 #[serde(skip_serializing_if = "Option::is_none")]
577 pub order_seq: Option<i64>,
578 #[serde(skip_serializing_if = "Option::is_none")]
579 pub searchable_word: Option<String>,
580 #[serde(skip_serializing_if = "Option::is_none")]
581 pub extra: Option<String>,
582 #[serde(skip_serializing_if = "Option::is_none")]
583 pub topic: Option<String>,
584 #[serde(skip_serializing_if = "Option::is_none")]
585 pub stream_no: Option<String>,
586 #[serde(skip_serializing_if = "Option::is_none")]
587 pub stream_seq: Option<i64>,
588 #[serde(skip_serializing_if = "Option::is_none")]
589 pub stream_flag: Option<i64>,
590 #[serde(skip_serializing_if = "Option::is_none")]
591 pub msg_key: Option<String>,
592 #[serde(skip_serializing_if = "Option::is_none")]
593 pub expire: Option<i64>,
594}
595
596#[derive(Debug, Clone, Default, Serialize, Deserialize)]
597pub struct MessageStatusSyncPayload {
598 #[serde(skip_serializing_if = "Option::is_none")]
599 pub message_id: Option<u64>,
600 #[serde(skip_serializing_if = "Option::is_none")]
601 pub server_message_id: Option<u64>,
602 #[serde(skip_serializing_if = "Option::is_none")]
603 pub id: Option<u64>,
604 #[serde(skip_serializing_if = "Option::is_none")]
605 pub channel_id: Option<u64>,
606 #[serde(skip_serializing_if = "Option::is_none")]
607 pub channel_type: Option<i32>,
608 #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
609 pub type_field: Option<i32>,
610 #[serde(skip_serializing_if = "Option::is_none")]
611 pub conversation_type: Option<i32>,
612 #[serde(skip_serializing_if = "Option::is_none")]
613 pub status: Option<i32>,
614 #[serde(skip_serializing_if = "Option::is_none")]
615 pub readed: Option<bool>,
616 #[serde(skip_serializing_if = "Option::is_none")]
617 pub is_read: Option<bool>,
618 #[serde(skip_serializing_if = "Option::is_none")]
619 pub read: Option<bool>,
620}
621
622#[derive(Debug, Clone, Serialize, Deserialize)]
624pub struct SyncEntitiesResponse {
625 pub items: Vec<SyncEntityItem>,
626 pub next_version: u64,
628 #[serde(default)]
630 pub has_more: bool,
631 #[serde(skip_serializing_if = "Option::is_none")]
633 pub min_version: Option<u64>,
634}
635
636#[cfg(test)]
641mod tests {
642 use super::*;
643
644 #[test]
645 fn test_client_submit_request_serialization() {
646 let req = ClientSubmitRequest {
647 local_message_id: 123456789,
648 channel_id: 1001,
649 channel_type: 1,
650 last_pts: 100,
651 command_type: "send_message".to_string(),
652 payload: serde_json::json!({"text": "Hello"}),
653 client_timestamp: 1700000000000,
654 device_id: Some("device_001".to_string()),
655 };
656
657 let json = serde_json::to_string(&req).unwrap();
658 assert!(json.contains("local_message_id"));
659 assert!(json.contains("123456789"));
660 }
661
662 #[test]
663 fn test_server_decision() {
664 let decision_accepted = ServerDecision::Accepted;
665 let decision_transformed = ServerDecision::Transformed {
666 reason: "Content filtered".to_string(),
667 };
668 let decision_rejected = ServerDecision::Rejected {
669 reason: "Spam detected".to_string(),
670 };
671
672 assert_eq!(decision_accepted, ServerDecision::Accepted);
673 assert!(matches!(
674 decision_transformed,
675 ServerDecision::Transformed { .. }
676 ));
677 assert!(matches!(decision_rejected, ServerDecision::Rejected { .. }));
678 }
679}