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
91 changes: 81 additions & 10 deletions docs/account.md
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,42 @@ account.send_image(
)
```

#### `send_emoji_reaction(message_id, emoji_shortname, chat_id=None)`

React to a message with an emoji, the same as reacting to a message in Status App. The reaction is a **toggle** - calling the method again with the same emoji on the same message removes it, so the same call both sets and unsets the reaction.

Emojis are identified by their **shortname**, exactly as Status App names them (`:thumbsup:`, `:heart_eyes:`). The surrounding colons are optional - `thumbsup` and `:thumbsup:` are the same emoji - and the full list of supported shortnames is documented under [Emojis](./utils.md#emojis).

Passing `chat_id` is purely an **optimisation**. Without it the chat has to be resolved from the message first, which costs one extra round trip to the Status Backend per reaction - worth avoiding when reacting to many messages in a chat that is already known, such as inside a [`listen_messages`](./account.md#listen_messages) loop. A `chat_id` that does not match the message is rejected by the backend and raises a custom exception, so pass it only when it is certain.

| Name | Type | Required | Description |
|-----|-----|-----|-------------|
| `message_id` | `str` | Yes | The `id` of the message to react to. Message IDs can be obtained from the `id` key of [`get_messages`](./account.md#get_messageschat_id-start_timestampnone-end_timestampnone), from the `lastMessage` of a [`listen_messages`](./account.md#listen_messages) event, or directly from the return value of [`send_message`](./account.md#send_messagechat_id-message-reply_to_message_idnone) / [`send_image`](./account.md#send_imagechat_id-file_path-messagenone-reply_to_message_idnone). |
| `emoji_shortname` | `str` | Yes | The emoji shortname as in Status App, with or without the surrounding colons. See [Emojis](./utils.md#emojis) for all supported values. |
| `chat_id` | `str` | No | Identifier of the chat the message belongs to, as found in the [`chats`](./account.md#chats) property. When omitted (default), it is resolved from `message_id` with an extra call to the Status Backend. |

```python
from status_sdk import Account

account = Account()
params = {
"name": "status-app-bot",
"password": "SNTPUMP"
}
account.login(**params)

chat = account.chats[0]

# Messages are returned newest first, so this is the latest message in the chat
messages = account.get_messages(chat["id"])
latest = messages[0]

account.send_emoji_reaction(latest["id"], ":thumbsup:")

# Reacting with the same emoji again removes the reaction
account.send_emoji_reaction(latest["id"], ":thumbsup:")
```

#### `get_messages(chat_id, start_timestamp=None, end_timestamp=None)`

Retrieve messages from the specified chat within an optional time range. Messages are returned in **descending order** (newest to oldest). The method automatically paginates through the backend until all messages in the specified range are collected. This method is ideal for backfilling, [batch processing](https://aws.amazon.com/what-is/batch-processing/) or [micro batch processing](https://www.dremio.com/wiki/micro-batch-processing/).
Expand Down Expand Up @@ -609,9 +645,6 @@ Listen for new incoming messages **in real time**. This method yields raw messag

```python
from status_sdk import Account
# For terminal readability only
from rich import print as rprint
from rich.pretty import Pretty

account = Account()
params = {
Expand All @@ -621,20 +654,38 @@ params = {
account.login(**params)

for msg in account.listen_messages():
rprint(Pretty(msg))
print(msg)
```

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

#### `listen_contact_requests()`

Listen for incoming contact requests **in real time**.
Listen for contact requests **in real time**. Both **incoming** contact requests sent to the account and contact requests sent by the account that were **accepted** by the other user are yielded. Every yielded event carries a `request_type` key that tells the two apart:

| `request_type` | Meaning |
|-----|-----|
| `incoming` | Another user sent a contact request to the account. Approve it with [`add_contact`](./account.md#add_contactpublic_key-display_namenone). |
| `accepted` | Another user accepted a contact request that the account had sent. The contact is now mutual. |

```python
from status_sdk import Account

account = Account()
params = {
"name": "status-app-bot",
"password": "SNTPUMP"
}
account.login(**params)

for request in account.listen_contact_requests():
print(request)
```

Handle each type separately:

```python
from status_sdk import Account
# For terminal readability only
from rich import print as rprint
from rich.pretty import Pretty

account = Account()
params = {
Expand All @@ -644,7 +695,28 @@ params = {
account.login(**params)

for request in account.listen_contact_requests():
rprint(Pretty(request))
if request["request_type"] == "incoming":
print("New contact request received")
elif request["request_type"] == "accepted":
print("Contact request was accepted")
```

#### `listen_message_mentions()`

Listen for `@0x...` mentions **in real time**.

```python
from status_sdk import Account

account = Account()
params = {
"name": "status-app-bot",
"password": "SNTPUMP"
}
account.login(**params)

for mention in account.listen_message_mentions():
print(mention)
```

#### `add_contact(public_key, display_name=None)`
Expand Down Expand Up @@ -1750,7 +1822,6 @@ Returns `list[dict]` where each element represents a community.
| `verified` | `bool` | Whether the community is verified. |
| `tags` | `list[str]` | Tags associated with the community. |
| `is_member` | `bool` | Whether the account is currently a member of the community. |
| `joined` | `bool` | Whether the account has joined the community. |
| `joined_timestamp` | `datetime.datetime`<br>`None` | Timestamp when the account joined the community. `None` when the account has not joined. |
| `requested_timestamp` | `datetime.datetime`<br>`None` | Timestamp when the join request was submitted. `None` when no request was made. |
| `encrypted` | `bool` | Whether the community messaging is encrypted. |
Expand Down
48 changes: 42 additions & 6 deletions docs/community.md
Original file line number Diff line number Diff line change
Expand Up @@ -534,11 +534,11 @@ community.delete_channel("announcements")

Listen for join requests to the community **in real time**.

Returns a `Generator` that yields one `dict` per request event:
Returns a `Generator` that yields one `models.CommunityRequest` **dataclass** per request event, so the fields are reached as attributes (`request.state`) rather than dictionary keys:

| Key | Type | Description |
| Attribute | Type | Description |
|----|----|-------------|
| `request_id` | `str` | The join request id. Pass this to [`accept`](./community.md#acceptpending_request_id) or [`decline`](./community.md#declinepending_request_id). |
| `id` | `str` | The join request id. Pass this to [`accept`](./community.md#acceptpending_request_id) or [`decline`](./community.md#declinepending_request_id). |
| `state` | `str` | The state the request moved into - see the table below. |
| `public_key` | `str` | Public key of the requesting member. |

Expand Down Expand Up @@ -568,12 +568,12 @@ community = Community(account, url=url)

# Auto-accept everyone who asks to join
for request in community.listen_requests():
print(f"{request['public_key']}\t{request['state']}")
print(f"{request.public_key}\t{request.state}")

if request["state"] != "pending":
if request.state != "pending":
continue

community.accept(request["request_id"])
community.accept(request.id)
community["general"].send_message("Welcome to the community!")
```

Expand Down Expand Up @@ -1239,6 +1239,42 @@ message_id = channel.send_image("./meme-67.png", "Daily random meme")
print(f"Sent image: {message_id}")
```

### `send_emoji_reaction(message_id, emoji_shortname)`

React to a message in the channel with an emoji, the same as reacting to a message in Status App. The reaction is a **toggle** - calling the method again with the same emoji on the same message removes it, so the same call both sets and unsets the reaction.

Emojis are identified by their **shortname**, exactly as Status App names them (`:thumbsup:`, `:heart_eyes:`). The surrounding colons are optional - `thumbsup` and `:thumbsup:` are the same emoji - and the full list of supported shortnames is documented under [Emojis](./utils.md#emojis).

| Name | Type | Required | Description |
|-----|-----|-----|-------------|
| `message_id` | `str` | Yes | The `id` of the message to react to. Message IDs can be obtained from the `id` key of [`get_messages`](./community.md#get_messagesstart_timestampnone-end_timestampnone), or directly from the return value of [`send_message`](./community.md#send_messagemessage-reply_to_message_idnone) / [`send_image`](./community.md#send_imagefile_path-messagenone-reply_to_message_idnone). |
| `emoji_shortname` | `str` | Yes | The emoji shortname as in Status App, with or without the surrounding colons. See [Emojis](./utils.md#emojis) for all supported values. |

```python
from status_sdk import Account, Community

account = Account()
params = {
"name": "status-app-bot",
"password": "SNTPUMP"
}
account.login(**params)

url = "https://status.app/c/G3QAAMQn9ueHRsR3W5Ouuy25fkCxziknAIEkCbYAoC04HjyGeQ6X8k45q3GVeyZiksbd38tQ4S_EfhrJKhRV3sDvjhmrCuSoDBIf2QJiEKwAOZipxis8ntNRVyPhC5IoWaEsj9X4P5zw093pcLofZzTV2gM=#zQ3shZeEJqTC1xhGUjxuS4rtHSrhJ8vUYp64v6qWkLpvdy9L9"
community = Community(account, url=url)

channel = community["general"]

# Messages are returned newest first, so this is the latest message in the channel
messages = channel.get_messages()
latest = messages[0]

channel.send_emoji_reaction(latest["id"], ":thumbsup:")

# Reacting with the same emoji again removes the reaction
channel.send_emoji_reaction(latest["id"], ":thumbsup:")
```

### `get_messages(start_timestamp=None, end_timestamp=None)`

Retrieve messages from the channel within an optional time range. Messages are returned in **descending order** (newest to oldest).
Expand Down
34 changes: 34 additions & 0 deletions docs/group-chat.md
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,40 @@ message_id = group_chat.send_image("./meme-67.png", "Daily random meme")
print(f"Sent image: {message_id}")
```

### `send_emoji_reaction(message_id, emoji_shortname)`

React to a message in the group chat with an emoji, the same as reacting to a message in Status App. The reaction is a **toggle** - calling the method again with the same emoji on the same message removes it, so the same call both sets and unsets the reaction.

Emojis are identified by their **shortname**, exactly as Status App names them (`:thumbsup:`, `:heart_eyes:`). The surrounding colons are optional - `thumbsup` and `:thumbsup:` are the same emoji - and the full list of supported shortnames is documented under [Emojis](./utils.md#emojis).

| Name | Type | Required | Description |
|-----|-----|-----|-------------|
| `message_id` | `str` | Yes | The `id` of the message to react to. Message IDs can be obtained from the `id` key of [`get_messages`](./group-chat.md#get_messagesstart_timestampnone-end_timestampnone), or directly from the return value of [`send_message`](./group-chat.md#send_messagemessage-reply_to_message_idnone) / [`send_image`](./group-chat.md#send_imagefile_path-messagenone-reply_to_message_idnone). |
| `emoji_shortname` | `str` | Yes | The emoji shortname as in Status App, with or without the surrounding colons. See [Emojis](./utils.md#emojis) for all supported values. |

```python
from status_sdk import Account, GroupChat

account = Account()
params = {
"name": "status-app-bot",
"password": "SNTPUMP"
}
account.login(**params)

chat = [chat for chat in account.chats if chat["type"] == "group_chat"][0]
group_chat = GroupChat(account, chat["id"])

# Messages are returned newest first, so this is the latest message in the chat
messages = group_chat.get_messages()
latest = messages[0]

group_chat.send_emoji_reaction(latest["id"], ":thumbsup:")

# Reacting with the same emoji again removes the reaction
group_chat.send_emoji_reaction(latest["id"], ":thumbsup:")
```

### `delete_message(id)`

Delete one of your **own** messages from the group chat. The deletion is propagated to the other members, so the message disappears for everybody. You can only delete messages that the logged-in account has sent.
Expand Down
Loading
Loading