Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,21 @@

All notable changes to `status-python-sdk` will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.4.0] - 2026-10-14

### Added

- Completed Enum for Content Type
- Function `listen_events` that listen to all event by default

### Changed

- `listen_messages` don't take any arguement and return all message of type `TEXT`, `STICKER`, `EMOJI`, `IMAGE`, `BRIDGED`

### Fixed

- Remove `image_path` from `content` of `Message` object

## [1.3.0] - 2026-10-11

### Added
Expand Down Expand Up @@ -242,6 +257,7 @@ will try to log in without a hashed password
- Launch [status-go] Docker container with Python instead of manual `docker compose up -d` setup.
- Custom library errors

[1.4.0]: https://github.com/status-im/status-python-sdk/releases/tag/1.4.0
[1.3.0]: https://github.com/status-im/status-python-sdk/releases/tag/1.3.0
[1.2.3]: https://github.com/status-im/status-python-sdk/releases/tag/1.2.3
[1.2.2]: https://github.com/status-im/status-python-sdk/releases/tag/1.2.2
Expand Down
17 changes: 9 additions & 8 deletions docs/account.md
Original file line number Diff line number Diff line change
Expand Up @@ -724,13 +724,13 @@ deleted = account.delete_message(message_id)
print(f"Deleted: {deleted}")
```

#### `listen_messages(content=None)`
#### `listen_events(content=[])`

Listen for new incoming messages **in real time**. This method is ideal for developing real time chat applications.
Listen for new events **in real time**, not only the messages. To listen to specifc events only, specify then in the `content` parameters

| Name | Type | Required | Description |
|-----|-----|-----|-------------|
| `content` | `list[types.Content]`<br>`types.Content` | No | Only yield messages of the given [content type(s)](./types.md#content). |
| `content` | `list[types.Content]`<br>`types.Content` | No | Only yield event of the given [content type(s)](./types.md#content). |

```python
from status_sdk import Account
Expand All @@ -746,10 +746,13 @@ for msg in account.listen_messages():
print(msg.content)
```

Only listen for text and emoji messages:
#### `listen_messages()`

Listen for new incoming messages **in real time**. This method is ideal for developing real time chat applications.


```python
from status_sdk import Account, types
from status_sdk import Account

account = Account()
params = {
Expand All @@ -758,12 +761,10 @@ params = {
}
account.login(**params)

for msg in account.listen_messages(content=[types.Content.TEXT, types.Content.EMOJI]):
for msg in account.listen_messages():
print(msg.content)
```

**Note**: If you receive multiple messages at once, `contacts` and `chats` will grow.

#### `listen_contact_requests()`

Listen for contact requests **in real time**. This method yields [`ContactRequest`](./models.md#contactrequest) objects, whose `incoming`, `accepted` and `removed` booleans tell the events apart:
Expand Down
4 changes: 2 additions & 2 deletions docs/types.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,15 @@ params = {
account.login(**params)

# Only yield text and emoji messages
for msg in account.listen_messages(content=[types.Content.TEXT, types.Content.EMOJI]):
for msg in account.listen_events(content=[types.Content.TEXT, types.Content.EMOJI]):
print(msg.content)
```

A single value can be passed instead of a list:

```python
# Only yield images (and albums)
for msg in account.listen_messages(content=types.Content.IMAGE):
for msg in account.listen_events(content=types.Content.IMAGE):
print(msg.image_urls)
```

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "status-sdk"
version = "1.3.0"
version = "1.4.0"
description = "Private chat. Communities. Multi-chain wallet. Browser. dApps all in one app, powered by SNT."
readme = "README.md"
requires-python = ">=3.11"
Expand Down
59 changes: 52 additions & 7 deletions status_sdk/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -967,16 +967,17 @@ def listen_message_mentions(self) -> Generator[models.Message, None, None]:
if mention_everyone in current_text or account_mention in current_text:
yield models.Message.from_raw(event["body"]["message"])

def listen_messages(self, content: Optional[Union[list[types.Content], types.Content]] = None) -> Generator[models.Message, None, None]:
def listen_messages(self) -> Generator[models.Message, None, None]:
"""
Listen for new messages continuously. Can be used for real time processing.
"""
if isinstance(content, type(None)):
content = []
elif isinstance(content, types.Content):
content = [content]

allowed_content_types = [content_type.value for content_type in (content if content else types.Content)]
allowed_content_types = [
types.Content.TEXT,
types.Content.STICKER,
types.Content.EMOJI,
types.Content.IMAGE,
types.Content.BRIDGED
]
albums: dict[str, list[dict]] = {}
processed: list[str] = []
SIZE = 100
Expand Down Expand Up @@ -1011,6 +1012,50 @@ def listen_messages(self, content: Optional[Union[list[types.Content], types.Con
if len(processed) == SIZE:
processed = []



def listen_events(self, content: Optional[Union[list[types.Content], types.Content]] = None) -> Generator[models.Message, None, None]:
"""
Listen for all events continuously. Can be used for real time processing.
"""
if isinstance(content, type(None)):
content = []
elif isinstance(content, types.Content):
content = [content]

allowed_content_types = [content_type.value for content_type in (content if content else types.Content)]
albums: dict[str, list[dict]] = {}
for message in self.signal.listen("messages.new"):
event: dict = message.get("event", {})
raw_messages: list[dict] = event.get("messages", [])
self.logger.debug(f"Received Message {json.dumps(raw_messages)}")
for raw in raw_messages:
if raw["contentType"] not in allowed_content_types:
continue
# Edge case with when receiving an Edit of a message with a reply
if "outgoingStatus" in raw and raw["outgoingStatus"] == "delivered":
continue

if not raw.get("albumId"):
yield models.Message.from_raw(raw)
continue

album_id: str = raw["albumId"]
if album_id not in albums:
albums[album_id] = []

albums[album_id].append(raw)
if len(albums[album_id]) != raw["albumImagesCount"]:
continue

raw["image"] = [
point["image"]
for point in albums[album_id]
]
yield models.Message.from_raw(raw)
albums.pop(album_id)


def get_messages(self, chat_id: str, start_timestamp: Optional[Union[str, datetime.datetime, datetime.date, pd.Timestamp]] = None, end_timestamp: Optional[Union[str, datetime.datetime, datetime.date, pd.Timestamp]] = None) -> list[dict]:
"""
Get all of the messages in the given start and end timestamps.
Expand Down
10 changes: 6 additions & 4 deletions status_sdk/models/message.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ class Message:
image_urls: list[str] = field(default_factory=list)
reply_id: Optional[str] = None
bridge_id: Optional[str] = None
edited_at: Optional[str] = None
payment_requests: list[PaymentRequest] = field(default_factory=list)

@classmethod
Expand All @@ -92,9 +93,13 @@ def from_raw(cls, raw: dict) -> Self:
"from_public_key": raw["from"],
"timestamp": datetime.datetime.fromtimestamp(raw["whisperTimestamp"] / 1_000),
"source": raw.get("bridgeMessage", {}).get("bridgeName", "status"),
"image_urls": []
"image_urls": [],
"content_type": raw["contentType"],
"content": raw["text"]
Comment thread
apentori marked this conversation as resolved.
}

print(f"Transforming raw message {raw}")

if len(raw["responseTo"]) > 0:
params["reply_id"] = raw["responseTo"]

Expand Down Expand Up @@ -123,9 +128,6 @@ def from_raw(cls, raw: dict) -> Self:

img_paths: list[str] = raw["image"]
params["image_urls"] += img_paths
text = raw["text"]
caption = f"{text}\n\n" if len(text) > 0 else ""
params["content"] = caption + "\n".join(img_paths)
params["content_type"] = types.Content.IMAGE
# Bridged Message
elif content_type == types.Content.BRIDGED.value:
Expand Down
5 changes: 5 additions & 0 deletions status_sdk/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,15 @@
class Content(Enum):
TEXT = 1
STICKER = 2
STATUS = 3
Comment thread
apentori marked this conversation as resolved.
EMOJI = 4
IMAGE = 7
CONTACT_REQUEST = 11
ACCEPTED_REQUEST = 16
REMOVED_CONTACT = 17
Comment thread
apentori marked this conversation as resolved.
BRIDGED = 18


class Chat(Enum):
PRIVATE = 1
COMMUNITY = 2
Expand Down
Loading