use serde::{Serialize, Deserialize};
use uuid::Uuid;
use crate::api::models::AsyncFile;
use super::*;
#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)]
pub struct CommandRequest<TData, TMetaData> {
pub sync_id: Uuid,
pub source_sync_id: Option<Uuid>,
#[serde(bound(deserialize = "for<'a> Command<TData, TMetaData>: Deserialize<'a>"))]
pub command: Command<TData, TMetaData>,
pub attachments: Vec<Attachment>,
pub from: From,
pub async_files: Vec<AsyncFile>,
pub bot_id: Uuid,
pub proto_version: u16,
pub entities: Vec<CommandEntities>,
}
#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)]
#[serde(untagged)]
pub enum Command<TData, TMetaData> {
#[serde(rename(serialize = "system:chat_created", deserialize = "system:chat_created"))]
#[serde(bound(deserialize = "for<'a> ChatCreatedCommand<TMetaData>: Deserialize<'a>"))]
ChatCreated(ChatCreatedCommand<TMetaData>),
#[serde(rename(serialize = "system:added_to_chat", deserialize = "system:added_to_chat"))]
#[serde(bound(deserialize = "for<'a> AddedToChatCommand<TMetaData>: Deserialize<'a>"))]
AddedToChat(AddedToChatCommand<TMetaData>),
#[serde(rename(serialize = "system:deleted_from_chat", deserialize = "system:deleted_from_chat"))]
#[serde(bound(deserialize = "for<'a> DeletedFromChatCommand<TMetaData>: Deserialize<'a>"))]
DeletedFromChat(DeletedFromChatCommand<TMetaData>),
#[serde(rename(serialize = "system:left_from_chat", deserialize = "system:left_from_chat"))]
#[serde(bound(deserialize = "for<'a> LeftFromChatCommand<TMetaData>: Deserialize<'a>"))]
LeftFromChat(LeftFromChatCommand<TMetaData>),
#[serde(rename(serialize = "system:smartapp_event", deserialize = "system:smartapp_event"))]
#[serde(bound(deserialize = "for<'a> SmartappEventCommand<TMetaData>: Deserialize<'a>"))]
SmartappEvent(SmartappEventCommand<TMetaData>),
#[serde(rename(serialize = "system:internal_bot_notification", deserialize = "system:internal_bot_notification"))]
#[serde(bound(deserialize = "for<'a> InternalBotNotificationCommand<TMetaData>: Deserialize<'a>"))]
InternalBotNotification(InternalBotNotificationCommand<TMetaData>),
#[serde(rename(serialize = "system:cts_login", deserialize = "system:cts_login"))]
#[serde(bound(deserialize = "for<'a> CtsLoginCommand<TMetaData>: Deserialize<'a>"))]
CtsLogin(CtsLoginCommand<TMetaData>),
#[serde(rename(serialize = "system:cts_logout", deserialize = "system:cts_logout"))]
#[serde(bound(deserialize = "for<'a> CtsLogoutCommand<TMetaData>: Deserialize<'a>"))]
CtsLogout(CtsLogoutCommand<TMetaData>),
#[serde(bound(deserialize = "for<'a> MessageCommand<TData, TMetaData>: Deserialize<'a>"))]
Message(MessageCommand<TData, TMetaData>),
}
#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)]
#[serde(tag = "body")]
pub struct ChatCreatedCommand<TMetaData> {
pub data: ChatCreatedCommandData,
#[serde(bound(deserialize = "for<'a> TMetaData: Deserialize<'a>"))]
pub metadata: TMetaData,
pub command_type: CommandType,
}
#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)]
#[serde(tag = "body")]
pub struct AddedToChatCommand<TMetaData> {
pub data: AddedToChatCommandData,
#[serde(bound(deserialize = "for<'a> TMetaData: Deserialize<'a>"))]
pub metadata: TMetaData,
pub command_type: CommandType,
}
#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)]
#[serde(tag = "body")]
pub struct DeletedFromChatCommand<TMetaData> {
pub data: DeletedFromChatCommandData,
#[serde(bound(deserialize = "for<'a> TMetaData: Deserialize<'a>"))]
pub metadata: TMetaData,
pub command_type: CommandType,
}
#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)]
#[serde(tag = "body")]
pub struct LeftFromChatCommand<TMetaData> {
pub data: LeftFromChatCommandData,
#[serde(bound(deserialize = "for<'a> TMetaData: Deserialize<'a>"))]
pub metadata: TMetaData,
pub command_type: CommandType,
}
#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)]
#[serde(tag = "body")]
pub struct SmartappEventCommand<TMetaData> {
pub data: SmartappEventCommandData,
#[serde(bound(deserialize = "for<'a> TMetaData: Deserialize<'a>"))]
pub metadata: TMetaData,
pub command_type: CommandType,
}
#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)]
#[serde(tag = "body")]
pub struct InternalBotNotificationCommand<TMetaData> {
pub data: InternalBotNotificationCommandData,
#[serde(bound(deserialize = "for<'a> TMetaData: Deserialize<'a>"))]
pub metadata: TMetaData,
pub command_type: CommandType,
}
#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)]
#[serde(tag = "body")]
pub struct CtsLoginCommand<TMetaData> {
pub data: CtsLoginCommandData,
#[serde(bound(deserialize = "for<'a> TMetaData: Deserialize<'a>"))]
pub metadata: TMetaData,
pub command_type: CommandType,
}
#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)]
#[serde(tag = "body")]
pub struct CtsLogoutCommand<TMetaData> {
pub data: CtsLogoutCommandData,
#[serde(bound(deserialize = "for<'a> TMetaData: Deserialize<'a>"))]
pub metadata: TMetaData,
pub command_type: CommandType,
}
#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)]
pub struct MessageCommand<TData, TMetaData> {
pub body: String,
#[serde(bound(deserialize = "for<'a> TData: Deserialize<'a>"))]
pub data: TData,
#[serde(bound(deserialize = "for<'a> TMetaData: Deserialize<'a>"))]
pub metadata: TMetaData,
pub command_type: CommandType,
}
#[cfg(test)]
mod tests {
use std::str::FromStr;
use crate::api::models::{Mention, MentionType, MentionData, PersonMentionData};
use super::*;
#[derive(Debug, serde::Serialize, serde::Deserialize, Eq, PartialEq)]
struct MetaData {
pub account_id: u32,
}
#[derive(Debug, serde::Serialize, serde::Deserialize, Eq, PartialEq)]
struct EmptyData { }
#[derive(Debug, serde::Serialize, serde::Deserialize, Eq, PartialEq)]
struct EmptyMetaData { }
#[test]
fn deserialize_message_command_ok() {
let request_str = r#"{
"sync_id": "a465f0f3-1354-491c-8f11-f400164295cb",
"command": {
"body": "/doit #6",
"command_type": "user",
"data": {},
"metadata": {"account_id": 94}
},
"attachments": [
{
"type": "image",
"data": {
"content": "data:image/jpg;base64,eDnXAc1FEUB0VFEFctII3lRlRBcetROeFfduPmXxE/8=",
"file_name": "image.jpg"
}
}
],
"async_files": [],
"from": {
"user_huid": "ab103983-6001-44e9-889e-d55feb295494",
"group_chat_id": "8dada2c8-67a6-4434-9dec-570d244e78ee",
"ad_login": "example_login",
"ad_domain": "example.com",
"username": "Bob",
"is_admin": true,
"is_creator": true,
"chat_type": "group_chat",
"host": "cts.ccteam.ru",
"app_version": "1.21.11",
"device": "Chrome 92.0",
"device_software": "macOS 10.15.7",
"device_meta": {
"permissions": {"microphone": true, "notifications": true},
"pushes": false,
"timezone": "Europe/Samara"
},
"platform": "web",
"locale": "en",
"manufacturer": "Google",
"platform_package_id": "ru.unlimitedtech.express"
},
"bot_id": "dcfa5a7c-7cc4-4c89-b6c0-80325604f9f4",
"proto_version": 4,
"entities": [
{
"type": "mention",
"data": {
"mention_type": "contact",
"mention_id": "c06a96fa-7881-0bb6-0e0b-0af72fe3683f",
"mention_data": {
"user_huid": "ab103983-6001-44e9-889e-d55feb295494",
"name": "Вася Иванов",
"conn_type": "cts"
}
}
}
]
}"#;
let command_request: CommandRequest::<EmptyData, MetaData> = serde_json::from_str(request_str).unwrap();
let expected_command_request = CommandRequest::<EmptyData, MetaData> {
sync_id: uuid::Uuid::from_str("a465f0f3-1354-491c-8f11-f400164295cb").unwrap(),
source_sync_id: None,
command: Command::Message(MessageCommand {
body: "/doit #6".to_string(),
command_type: CommandType::User,
data: EmptyData{},
metadata: MetaData {
account_id: 94,
},
}),
attachments: vec![Attachment::Image(ImageAttachment {
content: "data:image/jpg;base64,eDnXAc1FEUB0VFEFctII3lRlRBcetROeFfduPmXxE/8=".to_string(),
file_name: Some("image.jpg".to_string()),
})],
async_files: vec![],
from: From {
user_huid: Some(uuid::Uuid::from_str("ab103983-6001-44e9-889e-d55feb295494").unwrap()),
group_chat_id: Some(uuid::Uuid::from_str("8dada2c8-67a6-4434-9dec-570d244e78ee").unwrap()),
ad_login: Some("example_login".to_string()),
ad_domain: Some("example.com".to_string()),
username: Some("Bob".to_string()),
is_admin: Some(true),
is_creator: Some(true),
chat_type: Some(ChatType::GroupChat),
host: Some("cts.ccteam.ru".to_string()),
app_version: Some("1.21.11".to_string()),
device: Some("Chrome 92.0".to_string()),
device_software: Some("macOS 10.15.7".to_string()),
device_meta: Some(DeviceMeta {
permissions: Some(Permissions {
microphone: Some(true),
notifications: Some(true),
contacts: None,
storage: None,
}),
pushes: Some(false),
timezone: Some("Europe/Samara".to_string())
}),
platform: Some(Platform::Web),
locale: Some("en".to_string()),
manufacturer: Some("Google".to_string()),
platform_package_id: Some("ru.unlimitedtech.express".to_string()),
},
bot_id: uuid::Uuid::from_str("dcfa5a7c-7cc4-4c89-b6c0-80325604f9f4").unwrap(),
proto_version: 4,
entities: vec![CommandEntities::Mention(Mention {
mention_type: Some(MentionType::Contact),
mention_id: uuid::Uuid::from_str("c06a96fa-7881-0bb6-0e0b-0af72fe3683f").unwrap(),
mention_data: Some(MentionData::PersonMention(PersonMentionData {
user_huid: uuid::Uuid::from_str("ab103983-6001-44e9-889e-d55feb295494").unwrap(),
name: "Вася Иванов".to_string(),
conn_type: "cts".to_string(),
})),
})],
};
assert_eq!(command_request, expected_command_request)
}
#[test]
fn deserialize_chat_created_command_ok() {
let request_str = r#"{
"sync_id": "a465f0f3-1354-491c-8f11-f400164295cb",
"command": {
"body": "system:chat_created",
"data": {
"group_chat_id": "8dada2c8-67a6-4434-9dec-570d244e78ee",
"chat_type": "group_chat",
"name": "Meeting Room",
"creator": "ab103983-6001-44e9-889e-d55feb295494",
"members": [
{
"huid": "ab103983-6001-44e9-889e-d55feb295494",
"name": null,
"user_kind": "user",
"admin": true
},
{
"huid": "dcfa5a7c-7cc4-4c89-b6c0-80325604f9f4",
"name": "Funny Bot",
"user_kind": "botx",
"admin": false
}
]
},
"command_type": "system",
"metadata": {}
},
"async_files": [],
"attachments": [],
"entities": [],
"from": {
"user_huid": null,
"group_chat_id": "8dada2c8-67a6-4434-9dec-570d244e78ee",
"ad_login": null,
"ad_domain": null,
"username": null,
"chat_type": "group_chat",
"manufacturer": null,
"device": null,
"device_software": null,
"device_meta": {},
"platform": null,
"platform_package_id": null,
"is_admin": null,
"is_creator": null,
"app_version": null,
"locale": "en",
"host": "cts.ccteam.ru"
},
"bot_id": "dcfa5a7c-7cc4-4c89-b6c0-80325604f9f4",
"proto_version": 4,
"source_sync_id": null
}"#;
let command_request: CommandRequest::<EmptyData, EmptyMetaData> = serde_json::from_str(request_str).unwrap();
let expected_command_request = CommandRequest::<EmptyData, EmptyMetaData> {
sync_id: uuid::Uuid::from_str("a465f0f3-1354-491c-8f11-f400164295cb").unwrap(),
command: Command::ChatCreated(ChatCreatedCommand {
data: ChatCreatedCommandData {
group_chat_id: uuid::Uuid::from_str("8dada2c8-67a6-4434-9dec-570d244e78ee").unwrap(),
chat_type: ChatType::GroupChat,
name: "Meeting Room".to_string(),
creator: uuid::Uuid::from_str("ab103983-6001-44e9-889e-d55feb295494").unwrap(),
members: vec![
ChatCreatedCommandMembers {
huid: uuid::Uuid::from_str("ab103983-6001-44e9-889e-d55feb295494").unwrap(),
name: None,
user_kind: UserKind::User,
admin: true
},
ChatCreatedCommandMembers {
huid: uuid::Uuid::from_str("dcfa5a7c-7cc4-4c89-b6c0-80325604f9f4").unwrap(),
name: Some("Funny Bot".to_string()),
user_kind: UserKind::Botx,
admin: false
}
]
},
command_type: CommandType::System,
metadata: EmptyMetaData { },
}),
async_files: vec![],
attachments: vec![],
entities: vec![],
from: From {
user_huid: None,
group_chat_id: Some(uuid::Uuid::from_str("8dada2c8-67a6-4434-9dec-570d244e78ee").unwrap()),
ad_login: None,
ad_domain: None,
username: None,
chat_type: Some(ChatType::GroupChat),
manufacturer: None,
device: None,
device_software: None,
device_meta: Some(DeviceMeta {
pushes: None,
timezone: None,
permissions: None
}),
platform: None,
platform_package_id: None,
is_admin: None,
is_creator: None,
app_version: None,
locale: Some("en".to_string()),
host: Some("cts.ccteam.ru".to_string()),
},
bot_id: uuid::Uuid::from_str("dcfa5a7c-7cc4-4c89-b6c0-80325604f9f4").unwrap(),
proto_version: 4,
source_sync_id: None,
};
assert_eq!(command_request, expected_command_request)
}
}