Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Block users #101

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
114 changes: 112 additions & 2 deletions pywa/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -567,6 +567,104 @@ def mark_message_as_read(
},
)

def block_users(
self,
phone_id: str,
users: tuple[str, ...],
) -> dict[str, str | dict[str, list]]:
"""
Block users from sending messages to the business.

- Read more at `developers.facebook.com <https://developers.facebook.com/docs/whatsapp/cloud-api/block-users#unblock-users>`_.

Return example::

{
'messaging_product': 'whatsapp',
'block_users': {
'removed_users': [
{'input': '123456789', 'wa_id': '123456789'}
]
}
}

Args:
phone_id: The ID of the phone number to block users on.
users: The list of users to block.

Returns:
The response from the WhatsApp Cloud API.
"""
return self._make_request(
method="POST",
endpoint=f"/{phone_id}/block_users",
json={
"messaging_product": "whatsapp",
"block_users": [{"user": user} for user in users],
},
)

def unblock_users(
self,
phone_id: str,
users: tuple[str, ...],
) -> dict[str, list[dict[str, str]]]:
"""
Unblock users from sending messages to the business.

- Read more at `developers.facebook.com <https://developers.facebook.com/docs/whatsapp/cloud-api/block-users#unblock-users>`_.

Return example::

{
'messaging_product': 'whatsapp',
'block_users': {
'removed_users': [
{'input': '123456789', 'wa_id': '123456789'}
]
}

Args:
phone_id: The ID of the phone number to unblock users on.
users: The list of users to unblock.

Returns:
The response from the WhatsApp Cloud API.
"""
return self._make_request(
method="DELETE",
endpoint=f"/{phone_id}/block_users",
json={
"messaging_product": "whatsapp",
"block_users": [{"user": user} for user in users],
},
)

def get_blocked_users(
self,
phone_id: str,
limit: int | None = None,
after: str | None = None,
) -> dict[str, list[dict[str, str]]]:
"""
Get the list of blocked users.

- Read more at `developers.facebook.com <https://developers.facebook.com/docs/whatsapp/cloud-api/block-users#get-list-of-blocked-numbers>`_.

Args:
phone_id: The ID of the phone number to get the blocked users from.
limit: The limit of the users to get.
after: The cursor to get the users after.

Returns:
The response from the WhatsApp Cloud API.
"""
return self._make_request(
method="GET",
endpoint=f"/{phone_id}/block_users",
params={k: v for k, v in {"limit": limit, "after": after}.items() if v},
)

def get_business_phone_number(
self,
phone_id: str,
Expand Down Expand Up @@ -1056,6 +1154,8 @@ def get_flows(
self,
waba_id: str,
fields: tuple[str, ...] | None = None,
limit: int | None = None,
after: str | None = None,
) -> dict[str, list[dict[str, Any]]]:
"""
Get all flows.
Expand All @@ -1065,6 +1165,8 @@ def get_flows(
Args:
waba_id: The ID of the WhatsApp Business Account.
fields: The fields to get.
limit: The limit of the flows to get.
after: The cursor to get the flows after.

Return example::

Expand All @@ -1086,11 +1188,19 @@ def get_flows(
}
"""
endpoint = f"/{waba_id}/flows"
if fields:
endpoint += f"?fields={','.join(fields)}"
params = {
k: v
for k, v in {
"fields": ",".join(fields) if fields else None,
"limit": limit,
"after": after,
}.items()
if v
}
return self._make_request(
method="GET",
endpoint=endpoint,
params=params,
)

def get_flow_assets(
Expand Down
112 changes: 105 additions & 7 deletions pywa/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import collections
import dataclasses
import datetime
import functools
import hashlib
import json
import logging
Expand Down Expand Up @@ -67,7 +68,13 @@
CreatedFlow,
)
from .types.sent_message import SentMessage, SentTemplate
from .types.others import InteractiveType
from .types.others import (
InteractiveType,
Result,
UsersBlockedResult,
UsersUnblockedResult,
User,
)
from .utils import FastAPI, Flask
from .server import Server

Expand Down Expand Up @@ -1428,6 +1435,89 @@ def mark_message_as_read(
message_id=message_id,
)["success"]

def block_users(
self, users: Iterable[str | int], phone_id: str | int | None = None
) -> UsersBlockedResult:
"""
Block users by phone ID.

Example:

>>> wa = WhatsApp(...)
>>> res = wa.block_users(users=['1234567890', '0987654321'])
>>> if res.errors: print(res.failed_users)

Args:
users: The phone IDs of the users to block.
phone_id: The phone ID to block the users from (optional, if not provided, the client's phone ID will be used).

Returns:
A dictionary with the status of the block operation.
"""
return UsersBlockedResult.from_dict(
self.api.block_users(
phone_id=helpers.resolve_phone_id_param(self, phone_id, "phone_id"),
users=tuple(str(phone_id) for phone_id in users),
)
)

def unblock_users(
self, users: Iterable[str | int], phone_id: str | int | None = None
) -> UsersUnblockedResult:
"""
Unblock users by phone ID.

Example:

>>> wa = WhatsApp(...)
>>> wa.unblock_users(users=['1234567890', '0987654321'])

Args:
users: The phone IDs of the users to unblock.
phone_id: The phone ID to unblock the users from (optional, if not provided, the client's phone ID will be used).
Returns:
A dictionary with the status of the unblock operation.
"""
return UsersUnblockedResult.from_dict(
self.api.unblock_users(
phone_id=helpers.resolve_phone_id_param(self, phone_id, "phone_id"),
users=tuple(str(phone_id) for phone_id in users),
)
)

def get_blocked_users(
self,
phone_id: str | int | None = None,
*,
limit: int | None = None,
batch_size: int | None = None,
) -> Result[User]:
"""
Get the list of blocked users.

Example:

>>> wa = WhatsApp(...)
>>> for user in wa.get_blocked_users(): print(user)

Args:
phone_id: The phone ID to get the list of blocked users from (optional, if not provided, the client's phone ID will be used).
limit: The maximum number of users to return (optional, if not provided, all users will be returned).
batch_size: The number of users to return per request (optional).

Returns:
A Result object with the list of blocked users. You can iterate over the result to get the users.
"""
return Result(
fetcher=functools.partial(
self.api.get_blocked_users,
phone_id=helpers.resolve_phone_id_param(self, phone_id, "phone_id"),
),
item_factory=User.from_dict,
total_limit=limit,
batch_size=batch_size,
)

def upload_media(
self,
media: str | pathlib.Path | bytes | BinaryIO,
Expand Down Expand Up @@ -2288,7 +2378,10 @@ def get_flows(
invalidate_preview: bool = True,
waba_id: str | int | None = None,
phone_number_id: str | int | None = None,
) -> tuple[FlowDetails, ...]:
*,
limit: int | None = None,
batch_size: int | None = None,
) -> Result[FlowDetails]:
"""
Get the details of all flows belonging to the WhatsApp Business account.

Expand All @@ -2298,19 +2391,24 @@ def get_flows(
invalidate_preview: Whether to invalidate the preview (optional, default: True).
waba_id: The WhatsApp Business account ID (Overrides the client's business account ID).
phone_number_id: To check that the flows can be used with a specific phone number (optional).
limit: The maximum number of flows to return (optional).
batch_size: The number of flows to return in each batch (optional).

Returns:
The details of all flows.
A Result object with the list of flows. You can iterate over the result to get the flows.
"""
return tuple(
FlowDetails.from_dict(data=data, client=self)
for data in self.api.get_flows(
return Result(
fetcher=functools.partial(
self.api.get_flows,
waba_id=helpers.resolve_waba_id_param(self, waba_id),
fields=helpers.get_flow_fields(
invalidate_preview=invalidate_preview,
phone_number_id=phone_number_id,
),
)["data"]
),
item_factory=functools.partial(FlowDetails.from_dict, client=self),
total_limit=limit,
batch_size=batch_size,
)

def get_flow_metrics(
Expand Down
33 changes: 32 additions & 1 deletion pywa/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,7 @@ class FlowError(WhatsAppError):
"""
Base exception for all flow errors.

Read more at `developers.facebook.com <https://developers.facebook.com/docs/whatsapp/flows/reference/error-codes>`_.
- Read more at `developers.facebook.com <https://developers.facebook.com/docs/whatsapp/flows/reference/error-codes>`_.
"""

__error_codes__ = None
Expand Down Expand Up @@ -458,3 +458,34 @@ class FlowDeletingError(FlowError):
"""

__error_codes__ = (139004,)


# ====================================================================================================


class BlockUserError(WhatsAppError):
"""
Base exception for all block user errors.

- Read more at `developers.facebook.com <https://developers.facebook.com/docs/whatsapp/cloud-api/block-users#error-codes>`_.
"""

__error_codes__ = None


class BulkBlockingFailed(BlockUserError):
"""Bulk blocking failed to block some or all of the users."""

__error_codes__ = (139100,)


class BlockListLimitReached(BlockUserError):
"""The blocklist limit is reached when the 64k limit is met."""

__error_codes__ = (139101,)


class BlockListConcurrentUpdate(BlockUserError):
"""Occurs when the block list is updated while performing a pagination request and version_id does not match."""

__error_codes__ = (139102,)
1 change: 1 addition & 0 deletions pywa/types/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
Command,
ConversationalAutomation,
QRCode,
Result,
)
from .template import (
NewTemplate,
Expand Down
Loading
Loading