urbit_http_api/traits/
messaging.rs1use crate::error::{Result, UrbitAPIError};
2use crate::graph::{Node, NodeContents};
3use crate::Channel;
4use crossbeam::channel::{unbounded, Receiver};
5use json::JsonValue;
6use std::thread;
7use std::time::Duration;
8
9pub type Message = NodeContents;
14
15#[derive(Clone, Debug)]
17pub struct AuthoredMessage {
18 pub author: String,
19 pub contents: Message,
20 pub time_sent: String,
21 pub index: String,
22}
23
24impl AuthoredMessage {
25 pub fn new(author: &str, contents: &Message, time_sent: &str, index: &str) -> Self {
27 AuthoredMessage {
28 author: author.to_string(),
29 contents: contents.clone(),
30 time_sent: time_sent.to_string(),
31 index: index.to_string(),
32 }
33 }
34 pub fn from_node(node: &Node) -> Self {
36 Self::new(
37 &node.author,
38 &node.contents,
39 &node.time_sent_formatted(),
40 &node.index,
41 )
42 }
43
44 pub fn to_formatted_string(&self) -> String {
47 let content = self.contents.to_formatted_string();
48 format!("{} - ~{}:{}", self.time_sent, self.author, content)
49 }
50}
51
52pub trait Messaging {
54 fn channel(&mut self) -> &mut Channel;
56
57 fn send_message(
60 &mut self,
61 resource_ship: &str,
62 resource_name: &str,
63 message: &Message,
64 ) -> Result<String> {
65 let node = self.channel().graph_store().new_node(message);
66
67 if let Ok(_) = self
68 .channel()
69 .graph_store()
70 .add_node(resource_ship, resource_name, &node)
71 {
72 Ok(node.index)
73 } else {
74 Err(UrbitAPIError::FailedToSendChatMessage(
75 message.to_json().dump(),
76 ))
77 }
78 }
79
80 fn export_message_log(
82 &mut self,
83 resource_ship: &str,
84 resource_name: &str,
85 ) -> Result<Vec<String>> {
86 let mut export_log = vec![];
87 let authored_messages = self.export_authored_messages(resource_ship, resource_name)?;
88
89 for am in authored_messages {
90 if !am.contents.is_empty() {
91 export_log.push(am.to_formatted_string());
92 }
93 }
94
95 Ok(export_log)
96 }
97
98 fn export_authored_messages(
100 &mut self,
101 resource_ship: &str,
102 resource_name: &str,
103 ) -> Result<Vec<AuthoredMessage>> {
104 let mut authored_messages = vec![];
105 let nodes = self.export_message_nodes(resource_ship, resource_name)?;
106
107 for node in nodes {
108 if !node.contents.is_empty() {
109 let authored_message = AuthoredMessage::from_node(&node);
110 authored_messages.push(authored_message);
111 }
112 }
113
114 Ok(authored_messages)
115 }
116
117 fn export_message_nodes(
119 &mut self,
120 resource_ship: &str,
121 resource_name: &str,
122 ) -> Result<Vec<Node>> {
123 let messages_graph = &self
124 .channel()
125 .graph_store()
126 .get_graph(resource_ship, resource_name)?;
127
128 let mut nodes = messages_graph.clone().nodes;
129 nodes.sort_by(|a, b| a.time_sent.cmp(&b.time_sent));
130
131 Ok(nodes)
132 }
133
134 fn subscribe_to_messages(
142 &mut self,
143 resource_ship: &str,
144 resource_name: &str,
145 ) -> Result<Receiver<AuthoredMessage>> {
146 let resource_ship = resource_ship.to_string();
147 let resource_name = resource_name.to_string();
148 let (s, r) = unbounded();
150 let mut new_channel = self.channel().ship_interface.create_channel()?;
153
154 thread::spawn(move || {
155 let channel = &mut new_channel;
157 channel
158 .create_new_subscription("graph-store", "/updates")
159 .ok();
160 loop {
161 channel.parse_event_messages();
162 let res_graph_updates = &mut channel.find_subscription("graph-store", "/updates");
163 if let Some(graph_updates) = res_graph_updates {
164 loop {
167 let pop_res = graph_updates.pop_message();
168 if let Some(mess) = &pop_res {
170 if let Ok(json) = json::parse(mess) {
172 if !check_resource_json(&resource_ship, &resource_name, &json) {
175 continue;
176 }
177 if let Ok(node) = Node::from_graph_update_json(&json) {
179 let authored_message = AuthoredMessage::from_node(&node);
181 let _ = s.send(authored_message);
182 }
183 }
184 }
185 if let None = &pop_res {
187 break;
188 }
189 }
190 }
191 thread::sleep(Duration::new(0, 500000000));
193 }
194 });
195 Ok(r)
196 }
197}
198
199fn check_resource_json(
201 resource_ship: &str,
202 resource_name: &str,
203 resource_json: &JsonValue,
204) -> bool {
205 let resource = resource_json["graph-update"]["add-nodes"]["resource"].clone();
206 let json_resource_name = format!("{}", resource["name"]);
207 let json_resource_ship = format!("~{}", resource["ship"]);
208 if json_resource_name == resource_name && json_resource_ship == resource_ship {
209 return true;
210 }
211 false
212}