iris_chat_protocol/protocol_engine/
engine_group_wakes.rs1use super::*;
2
3pub(super) const MAX_READY_GROUP_PUBLICATIONS: usize = 4_096;
4
5impl ProtocolEngine {
6 pub fn owns_group_data_stream(&self, chat_id: &str, author: PublicKey) -> bool {
7 let Some(group) = chat_id.strip_prefix("group:") else {
8 return false;
9 };
10 self.group_manager.group_has_member(group, self.local_owner) == Some(true)
11 && self.group_manager.is_local_sender_key_stream(
12 group,
13 ndr_device(author),
14 self.local_device,
15 )
16 }
17
18 pub fn subscribable_group_sender_event_pubkeys(&self) -> Vec<PublicKey> {
20 self.group_manager
21 .subscribable_sender_event_pubkeys()
22 .into_iter()
23 .filter_map(|a| public_device(a).ok())
24 .collect()
25 }
26
27 fn ready_group_publication_is_allowed(&self, pending: &ProtocolReadyGroupPublish) -> bool {
28 pending.publish.is_publishable()
31 && (pending.publish.carrier != ProtocolCarrier::SharedGroup
32 || self
33 .owns_group_data_stream(&pending.publish.chat_id, pending.publish.event.pubkey))
34 && !pending
35 .publish
36 .chat_id
37 .strip_prefix("group:")
38 .is_some_and(|group| self.local_owner_is_inactive_for_group(group))
39 && !pending
40 .wake_recipient
41 .is_some_and(|owner| self.held_direct_chats.contains(&owner.to_hex()))
42 }
43
44 pub(super) fn retire_ineligible_group_publications(&mut self) -> anyhow::Result<()> {
45 let rejected = self
46 .ready_group_publications
47 .iter()
48 .filter(|(_, pending)| !self.ready_group_publication_is_allowed(pending))
49 .map(|(id, _)| id.clone())
50 .collect::<Vec<_>>();
51 if rejected.is_empty() {
52 return Ok(());
53 }
54 self.with_state_checkpoint(|engine| {
55 for id in rejected {
56 engine.ready_group_publications.remove(&id);
57 }
58 engine.persist_now()
59 })
60 }
61
62 pub(super) fn retain_group_publications(
63 &mut self,
64 effects: &[ProtocolEffect],
65 wake_recipient: Option<NdrOwnerPubkey>,
66 ) -> anyhow::Result<()> {
67 let added = effects
68 .iter()
69 .filter(|effect| {
70 let ProtocolEffect::Publish(publish) = effect;
71 !self
72 .ready_group_publications
73 .contains_key(&publish.event.id.to_hex())
74 })
75 .count();
76 anyhow::ensure!(
77 self.ready_group_publications.len().saturating_add(added)
78 <= MAX_READY_GROUP_PUBLICATIONS,
79 "Outgoing group queue is full"
80 );
81 for effect in effects {
82 let ProtocolEffect::Publish(publish) = effect;
83 self.ready_group_publications
84 .entry(publish.event.id.to_hex())
85 .or_insert_with(|| ProtocolReadyGroupPublish {
86 publish: publish.clone(),
87 wake_recipient,
88 });
89 }
90 Ok(())
91 }
92
93 pub fn pending_group_publish_effects(&self) -> Vec<ProtocolEffect> {
96 self.ready_group_publications
97 .values()
98 .filter(|pending| self.ready_group_publication_is_allowed(pending))
99 .map(|pending| ProtocolEffect::Publish(pending.publish.clone()))
100 .collect()
101 }
102
103 pub fn has_pending_group_publish(&self, event_id: &str) -> bool {
104 self.ready_group_publications.contains_key(event_id)
105 }
106
107 pub fn ack_group_publish_effects(&mut self, effects: &[ProtocolEffect]) -> anyhow::Result<()> {
110 if !effects.iter().any(|effect| {
111 let ProtocolEffect::Publish(publish) = effect;
112 self.has_pending_group_publish(&publish.event.id.to_hex())
113 }) {
114 return Ok(());
115 }
116 self.with_state_checkpoint(|engine| {
117 for effect in effects {
118 let ProtocolEffect::Publish(publish) = effect;
119 engine
120 .ready_group_publications
121 .remove(&publish.event.id.to_hex());
122 }
123 engine.persist_now()
124 })
125 }
126}