twetch_sdk/chat/
conversation.rs1use crate::{api::Api, chat::message::Message, wallet::Wallet};
2use anyhow::Result;
3use base64;
4use bsv_wasm::{ECIESCiphertext, Hash, PrivateKey, PublicKey, ECIES};
5use serde::*;
6use serde_json::json;
7use wasm_bindgen::prelude::*;
8
9#[wasm_bindgen]
10#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
11pub struct Conversation {
12 id: String,
13 key: String,
14}
15
16impl Conversation {
17 pub async fn create(token: String, user_ids: Vec<String>) -> Result<Conversation> {
18 let api = Api { token };
19 let pubkeys = api
20 .list_pubkeys(user_ids.clone())
21 .await?
22 .as_array()
23 .unwrap()
24 .clone();
25
26 let key = Conversation::generate_key();
27
28 let mut conversation_users = Vec::new();
29
30 for e in 0..user_ids.len() {
31 let pubkey = pubkeys[e]
32 .get("publicKey")
33 .unwrap()
34 .as_str()
35 .unwrap()
36 .to_string();
37 conversation_users.push(json!({
38 "userId": user_ids[e],
39 "encryptedKey": Conversation::encrypt_key(key.clone(), pubkey).unwrap()
40 }))
41 }
42
43 let payload =
44 json!({ "conversationUsers": serde_json::to_value(conversation_users).unwrap() })
45 .to_string();
46
47 let res = api.create_conversation(payload).await?;
48
49 Ok(Conversation {
50 id: res.get("id").unwrap().as_str().unwrap().to_string(),
51 key,
52 })
53 }
54
55 pub async fn create_message(&self, token: String, description: String) -> Result<Message> {
56 Message::create(
57 self.key.clone(),
58 self.id.clone(),
59 "1".to_string(),
60 description,
61 token,
62 )
63 .await
64 }
65}
66
67#[wasm_bindgen]
68impl Conversation {
69 #[wasm_bindgen(js_name = generateKey)]
70 pub fn generate_key() -> String {
71 Hash::sha_256(&PrivateKey::from_random().to_bytes())
72 .to_hex()
73 .chars()
74 .skip(32)
75 .take(32)
76 .collect()
77 }
78
79 #[wasm_bindgen(js_name = encrypt)]
80 pub fn encrypt_key(key: String, pubkey: String) -> Option<String> {
81 let public_key = match PublicKey::from_hex(&pubkey) {
82 Ok(v) => v,
83 Err(_) => return None,
84 };
85
86 let encrypted = match ECIES::encrypt_with_ephemeral_private_key(key.as_bytes(), &public_key)
87 {
88 Ok(v) => v.to_bytes(),
89 Err(_) => return None,
90 };
91
92 Some(base64::encode_config(encrypted, base64::STANDARD))
93 }
94
95 #[wasm_bindgen(js_name = decrypt)]
96 pub fn decrypt_key(encrypted_key: String, seed: String) -> Option<Vec<u8>> {
97 let wallet = Wallet::new(seed);
98 let xpriv = match wallet.xpriv_account() {
99 Some(v) => v,
100 None => return None,
101 };
102
103 let encrypted_key_buf = match base64::decode_config(encrypted_key, base64::STANDARD) {
104 Ok(v) => v,
105 Err(_) => return None,
106 };
107
108 let ciphertext = match ECIESCiphertext::from_bytes(&encrypted_key_buf, true) {
109 Ok(v) => v,
110 Err(_) => return None,
111 };
112
113 let pubkey = match ciphertext.extract_public_key() {
114 Ok(v) => v,
115 Err(_) => return None,
116 };
117
118 let decrypted = match ECIES::decrypt(&ciphertext, &xpriv.get_private_key(), &pubkey) {
119 Ok(v) => v,
120 Err(_) => return None,
121 };
122
123 return Some(decrypted);
124 }
125}