> ## Documentation Index
> Fetch the complete documentation index at: https://whatsapp-rust.jlucaso.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Blocking

> Block and unblock contacts, manage blocklist

The `Blocking` struct provides methods for blocking and unblocking contacts, as well as retrieving and checking the blocklist.

## Access

Access blocking operations through the client:

```rust theme={null}
let blocking = client.blocking();
```

## Methods

### block

Block a contact. Accepts either a LID or a PN JID; the client resolves the LID↔PN pair from its mapping cache and emits a stanza carrying both (`jid=LID`, `pn_jid=PN`). Modern WhatsApp servers reject PN-only block requests.

```rust theme={null}
pub async fn block(&self, jid: &Jid) -> Result<(), BlockingError>
```

**Parameters:**

* `jid` - Contact JID to block (LID or PN)

**Requirements:**

* A LID↔PN mapping for the target must exist in the client's mapping store. If no mapping is available, the call returns `BlockingError::InvalidJid`.

**Effects:**

* Contact will not be able to message you
* Contact will not see your presence updates
* Contact will not see your profile picture (depending on privacy settings)

**Example:**

```rust theme={null}
let contact: Jid = "15551234567@s.whatsapp.net".parse()?;
client.blocking().block(&contact).await?;
println!("Blocked {}", contact);
```

### unblock

Unblock a previously blocked contact. Accepts either a LID or a PN JID; PN input is internally resolved to the LID required on the wire.

```rust theme={null}
pub async fn unblock(&self, jid: &Jid) -> Result<(), BlockingError>
```

**Parameters:**

* `jid` - Contact JID to unblock (LID or PN)

**Example:**

```rust theme={null}
let contact: Jid = "15551234567@s.whatsapp.net".parse()?;
client.blocking().unblock(&contact).await?;
println!("Unblocked {}", contact);
```

### get\_blocklist

Retrieve the full list of blocked contacts.

```rust theme={null}
pub async fn get_blocklist(&self) -> Result<Vec<BlocklistEntry>, BlockingError>
```

**Returns:**

* `Vec<BlocklistEntry>` - All blocked contacts

**BlocklistEntry fields:**

* `jid: Jid` - Blocked contact JID
* `timestamp: Option<u64>` - Unix timestamp when blocked (if available)

**Example:**

```rust theme={null}
let blocklist = client.blocking().get_blocklist().await?;

println!("Blocked contacts: {}", blocklist.len());

for entry in blocklist {
    println!("  JID: {}", entry.jid);
    
    if let Some(ts) = entry.timestamp {
        println!("    Blocked at: {}", ts);
    }
}
```

### is\_blocked

Check if a specific contact is blocked. Accepts either a LID or a PN JID; the client resolves the LID↔PN pair from its mapping cache so a PN-input query correctly matches a LID-keyed blocklist entry (and vice versa).

```rust theme={null}
pub async fn is_blocked(&self, jid: &Jid) -> Result<bool, BlockingError>
```

**Parameters:**

* `jid` - Contact JID to check (LID or PN)

**Returns:**

* `bool` - `true` if blocked, `false` otherwise

**Behavior:**

* Compares only the user part of the JID (ignores device ID)
* Blocking applies to the entire user account, not individual devices
* Because `block()` stores entries keyed by LID, `is_blocked()` resolves the queried JID's LID/PN pair before matching. If the mapping lookup fails (network or backend error), the call returns `Err` rather than silently falling back to the raw user, which would risk a false negative. When no mapping exists for the JID, the raw user part is used as-is.

**Example:**

```rust theme={null}
let contact: Jid = "15551234567@s.whatsapp.net".parse()?;

if client.blocking().is_blocked(&contact).await? {
    println!("{} is blocked", contact);
} else {
    println!("{} is not blocked", contact);
}
```

## BlocklistEntry Type

```rust theme={null}
pub struct BlocklistEntry {
    pub jid: Jid,
    pub timestamp: Option<u64>,
}
```

**Fields:**

* `jid` - The blocked contact's JID
* `timestamp` - Unix timestamp (seconds since epoch) when the contact was blocked

The timestamp may be `None` if the server doesn't provide it.

## Error Types

### `BlockingError`

All methods return `Result<T, BlockingError>`:

```rust theme={null}
#[non_exhaustive]
pub enum BlockingError {
    #[error("{0}")]
    Iq(#[from] IqError),
    #[error("invalid blocklist target: {0}")]
    InvalidJid(String),
    #[error("{0}")]
    Internal(#[from] anyhow::Error),
}
```

**Variants:**

* `Iq` — Wraps an IQ request failure (timeout, server error, etc.)
* `InvalidJid` — The provided JID was not a valid blocklist target
* `Internal` — Internal error (network, encoding, etc.)

## Wire Format

### Block Request

The block stanza carries both the LID (in `jid`) and the PN (in `pn_jid`). Modern WhatsApp servers reject blocks that omit `pn_jid`.

```xml theme={null}
<iq xmlns="blocklist" type="set" to="s.whatsapp.net" id="...">
  <item action="block" jid="12345678901234@lid" pn_jid="15551234567@s.whatsapp.net"/>
</iq>
```

### Unblock Request

The unblock stanza only requires the LID; `pn_jid` is omitted.

```xml theme={null}
<iq xmlns="blocklist" type="set" to="s.whatsapp.net" id="...">
  <item action="unblock" jid="12345678901234@lid"/>
</iq>
```

### Get blocklist request

```xml theme={null}
<iq xmlns="blocklist" type="get" to="s.whatsapp.net" id="..."/>
```

### Blocklist Response

```xml theme={null}
<iq from="s.whatsapp.net" id="..." type="result">
  <list>
    <item jid="15551111111@s.whatsapp.net" t="1640000000"/>
    <item jid="15552222222@s.whatsapp.net" t="1640000100"/>
  </list>
</iq>
```

Or direct items without `<list>` wrapper:

```xml theme={null}
<iq from="s.whatsapp.net" id="..." type="result">
  <item jid="15551111111@s.whatsapp.net" t="1640000000"/>
  <item jid="15552222222@s.whatsapp.net" t="1640000100"/>
</iq>
```

## Usage Examples

### Block a Contact

```rust theme={null}
let contact: Jid = "15551234567@s.whatsapp.net".parse()?;

match client.blocking().block(&contact).await {
    Ok(_) => println!("Successfully blocked {}", contact),
    Err(e) => eprintln!("Failed to block: {}", e),
}
```

### Unblock a Contact

```rust theme={null}
let contact: Jid = "15551234567@s.whatsapp.net".parse()?;

match client.blocking().unblock(&contact).await {
    Ok(_) => println!("Successfully unblocked {}", contact),
    Err(e) => eprintln!("Failed to unblock: {}", e),
}
```

### Check before blocking

```rust theme={null}
let contact: Jid = "15551234567@s.whatsapp.net".parse()?;

if !client.blocking().is_blocked(&contact).await? {
    client.blocking().block(&contact).await?;
    println!("Contact blocked");
} else {
    println!("Contact already blocked");
}
```

### List all blocked contacts

```rust theme={null}
let blocklist = client.blocking().get_blocklist().await?;

if blocklist.is_empty() {
    println!("No blocked contacts");
} else {
    println!("Blocked contacts:");
    for entry in blocklist {
        print!("  - {}", entry.jid);
        if let Some(ts) = entry.timestamp {
            println!(" (blocked: {})", ts);
        } else {
            println!();
        }
    }
}
```

### Conditional Block/Unblock

```rust theme={null}
let contact: Jid = "15551234567@s.whatsapp.net".parse()?;
let should_block = true; // Your logic here

if should_block {
    if !client.blocking().is_blocked(&contact).await? {
        client.blocking().block(&contact).await?;
        println!("Blocked {}", contact);
    }
} else {
    if client.blocking().is_blocked(&contact).await? {
        client.blocking().unblock(&contact).await?;
        println!("Unblocked {}", contact);
    }
}
```

### Batch block multiple contacts

```rust theme={null}
let contacts_to_block = vec![
    "15551111111@s.whatsapp.net".parse()?,
    "15552222222@s.whatsapp.net".parse()?,
    "15553333333@s.whatsapp.net".parse()?,
];

for contact in contacts_to_block {
    match client.blocking().block(&contact).await {
        Ok(_) => println!("Blocked {}", contact),
        Err(e) => eprintln!("Failed to block {}: {}", contact, e),
    }
}
```

## Error Handling

```rust theme={null}
use whatsapp_rust::BlockingError;

let contact: Jid = "15551234567@s.whatsapp.net".parse()?;

match client.blocking().block(&contact).await {
    Ok(_) => println!("Blocked successfully"),
    Err(BlockingError::Iq(e)) => {
        eprintln!("IQ request failed: {}", e);
    }
    Err(BlockingError::InvalidJid(msg)) => {
        eprintln!("Invalid JID: {}", msg);
    }
    Err(e) => eprintln!("Error: {}", e),
}
```

## Device ID Handling

The `is_blocked()` method compares only the user part of JIDs, ignoring device IDs:

```rust theme={null}
let jid1: Jid = "15551234567.0@s.whatsapp.net".parse()?;  // Device 0
let jid2: Jid = "15551234567.1@s.whatsapp.net".parse()?;  // Device 1

// Block device 0
client.blocking().block(&jid1).await?;

// Check if device 1 is blocked (will return true)
assert!(client.blocking().is_blocked(&jid2).await?);

// Blocking applies to the user account, not specific devices
```

## Complete Example

```rust theme={null}
use whatsapp_rust::features::blocking::BlocklistEntry;

async fn manage_blocklist(client: &Client) -> anyhow::Result<()> {
    // Get current blocklist
    let blocklist = client.blocking().get_blocklist().await?;
    println!("Current blocklist: {} contacts", blocklist.len());
    
    // Block a new contact
    let spam_contact: Jid = "15559999999@s.whatsapp.net".parse()?;
    
    if !client.blocking().is_blocked(&spam_contact).await? {
        client.blocking().block(&spam_contact).await?;
        println!("Blocked spam contact");
    }
    
    // Unblock an old contact
    let old_friend: Jid = "15551234567@s.whatsapp.net".parse()?;
    
    if client.blocking().is_blocked(&old_friend).await? {
        client.blocking().unblock(&old_friend).await?;
        println!("Unblocked old friend");
    }
    
    // Print updated blocklist
    let updated_blocklist = client.blocking().get_blocklist().await?;
    println!("\nUpdated blocklist:");
    for entry in updated_blocklist {
        println!("  - {}", entry.jid);
    }
    
    Ok(())
}
```
