xmtp_mls/groups/
members.rs1use crate::{context::XmtpSharedContext, identity_updates::IdentityUpdates};
2
3use super::{GroupError, MlsGroup, validated_commit::extract_group_membership};
4use xmtp_db::prelude::*;
5use xmtp_db::{
6 StorageError,
7 consent_record::{ConsentState, ConsentType},
8};
9use xmtp_id::{
10 InboxId,
11 associations::{AssociationState, Identifier},
12};
13
14#[derive(Debug, Clone)]
15pub struct GroupMember {
16 pub inbox_id: InboxId,
17 pub account_identifiers: Vec<Identifier>,
18 pub installation_ids: Vec<Vec<u8>>,
19 pub permission_level: PermissionLevel,
20 pub consent_state: ConsentState,
21}
22
23#[derive(Debug, Clone, PartialEq, Eq)]
24pub enum PermissionLevel {
25 Member,
26 Admin,
27 SuperAdmin,
28}
29
30impl<Context> MlsGroup<Context>
31where
32 Context: XmtpSharedContext,
33{
34 pub async fn members(&self) -> Result<Vec<GroupMember>, GroupError> {
36 let db = self.context.db();
37 let group_membership = self.with_group_snapshot(|mls_group| {
38 Ok(extract_group_membership(mls_group.extensions())?)
39 })?;
40 let requests = group_membership
41 .members
42 .into_iter()
43 .map(|(inbox_id, sequence_id)| (inbox_id, sequence_id as i64))
44 .filter(|(_, sequence_id)| *sequence_id != 0) .collect::<Vec<_>>();
46
47 let association_states = db.batch_read_from_cache(requests.clone())?;
48 let mut association_states: Vec<AssociationState> = association_states
49 .into_iter()
50 .map(|a| a.try_into())
51 .collect::<Result<_, _>>()
52 .map_err(StorageError::from)?;
53 if association_states.len() != requests.len() {
54 let missing_requests: Vec<_> = requests
56 .iter()
57 .filter_map(|(id, sequence)| {
58 if association_states
60 .iter()
61 .any(|state| state.inbox_id() == id)
62 {
63 return None;
64 }
65 Some((id.as_str(), Some(*sequence)))
66 })
67 .collect();
68 let identity_updates = IdentityUpdates::new(&self.context);
69 let mut new_states = identity_updates
70 .batch_get_association_state(&db, &missing_requests)
71 .await?;
72 association_states.append(&mut new_states);
73
74 if association_states.len() != requests.len() {
75 tracing::error!(
79 "Failed to load all members for group - metadata: {:?}, computed members: {:?}",
80 requests,
81 association_states
82 );
83 return Err(GroupError::InvalidGroupMembership);
84 }
85 }
86 let mutable_metadata = self.mutable_metadata()?;
87 let members = association_states
88 .into_iter()
89 .map(|association_state| {
90 let inbox_id_str = association_state.inbox_id().to_string();
91 let is_admin = mutable_metadata.is_admin(&inbox_id_str);
92 let is_super_admin = mutable_metadata.is_super_admin(&inbox_id_str);
93 let permission_level = if is_super_admin {
94 PermissionLevel::SuperAdmin
95 } else if is_admin {
96 PermissionLevel::Admin
97 } else {
98 PermissionLevel::Member
99 };
100
101 let consent = db.get_consent_record(inbox_id_str.clone(), ConsentType::InboxId)?;
102
103 Ok(GroupMember {
104 inbox_id: inbox_id_str.clone(),
105 account_identifiers: association_state.identifiers(),
106 installation_ids: association_state.installation_ids(),
107 permission_level,
108 consent_state: consent.map_or(ConsentState::Unknown, |c| c.state),
109 })
110 })
111 .collect::<Result<Vec<GroupMember>, GroupError>>()?;
112
113 Ok(members)
114 }
115}