soblendwa 0.1.0

SoblendWA: a blazing fast WhatsApp Web multi-device client library written in Rust. Crafted by Soblend Development Studio.
Documentation
use super::groups::GroupInfo;
use super::Client;
use crate::binary::Node;
use crate::crypto;
use crate::error::{Error, Result};
use crate::jid::Jid;

#[derive(Clone, Debug)]
pub struct LinkedGroup {
    pub jid: Jid,
    pub subject: String,
    pub owner: Option<Jid>,
    pub created: i64,
    pub size: Option<u64>,
}

impl Client {
    fn community_iq(&self, to: Jid, kind: &'static str, body: Node) -> Node {
        Node::new("iq").attr("to", to).attr("type", kind).attr("xmlns", "w:g2").child_node(body)
    }

    pub async fn create_community(&self, subject: &str, description: &str) -> Result<GroupInfo> {
        let body = Node::new("create").attr("subject", subject.to_owned()).children([
            Node::new("description")
                .attr("id", hex::encode_upper(crypto::random_array::<6>()))
                .child_node(Node::new("body").bytes(description.as_bytes().to_vec())),
            Node::new("parent").attr("default_membership_approval_mode", "request_required"),
            Node::new("allow_non_admin_sub_group_creation"),
            Node::new("create_general_chat"),
        ]);
        let response = self.query(self.community_iq(Jid::group_server(), "set", body)).await?;
        GroupInfo::parse(response.child("group").ok_or_else(|| Error::binary("missing community node"))?)
    }

    pub async fn create_community_group(
        &self,
        community: &Jid,
        subject: &str,
        participants: &[Jid],
    ) -> Result<GroupInfo> {
        let mut children: Vec<Node> =
            participants.iter().map(|p| Node::new("participant").attr("jid", p.to_non_device())).collect();
        children.push(Node::new("linked_parent").attr("jid", community.clone()));
        let body = Node::new("create")
            .attr("subject", subject.to_owned())
            .attr("key", hex::encode_upper(crypto::random_array::<8>()))
            .children(children);
        let response = self.query(self.community_iq(Jid::group_server(), "set", body)).await?;
        GroupInfo::parse(response.child("group").ok_or_else(|| Error::binary("missing group node"))?)
    }

    pub async fn link_group(&self, community: &Jid, group: &Jid) -> Result<()> {
        let body = Node::new("links").child_node(
            Node::new("link").attr("link_type", "sub_group").child_node(Node::new("group").attr("jid", group.clone())),
        );
        self.query(self.community_iq(community.clone(), "set", body)).await?;
        self.forget_group(group).await;
        Ok(())
    }

    pub async fn unlink_group(&self, community: &Jid, group: &Jid) -> Result<()> {
        let body = Node::new("unlink")
            .attr("unlink_type", "sub_group")
            .child_node(Node::new("group").attr("jid", group.clone()));
        self.query(self.community_iq(community.clone(), "set", body)).await?;
        self.forget_group(group).await;
        Ok(())
    }

    pub async fn community_groups(&self, community: &Jid) -> Result<Vec<LinkedGroup>> {
        let response = self.query(self.community_iq(community.clone(), "get", Node::new("sub_groups"))).await?;
        Ok(response
            .child("sub_groups")
            .map(|list| {
                list.children_by("group")
                    .filter_map(|g| {
                        let id = g.attr_string("id")?;
                        Some(LinkedGroup {
                            jid: if id.contains('@') { id.parse().ok()? } else { Jid::group(id) },
                            subject: g.attr_string("subject").unwrap_or_default(),
                            owner: g.attr_jid("creator"),
                            created: g.attr_i64("creation").unwrap_or_default(),
                            size: g.attr_u64("size"),
                        })
                    })
                    .collect()
            })
            .unwrap_or_default())
    }

    pub async fn leave_community(&self, community: &Jid) -> Result<()> {
        let body = Node::new("leave").child_node(Node::new("community").attr("id", community.clone()));
        self.query(self.community_iq(Jid::group_server(), "set", body)).await?;
        self.forget_group(community).await;
        Ok(())
    }

    pub async fn set_community_join_approval(&self, community: &Jid, enabled: bool) -> Result<()> {
        let body = Node::new("membership_approval_mode")
            .child_node(Node::new("community_join").attr("state", if enabled { "on" } else { "off" }));
        self.query(self.community_iq(community.clone(), "set", body)).await?;
        self.forget_group(community).await;
        Ok(())
    }
}