account: Emoji reactions

- Related to https://github.com/status-im/status-python-sdk/issues/43
This commit is contained in:
Nick Ninov
2026-08-20 12:38:35 +03:00
parent c499ac7769
commit 6773698575
9 changed files with 3914 additions and 0 deletions
+36
View File
@@ -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/).
+36
View File
@@ -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).
+34
View File
@@ -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.
+1876
View File
File diff suppressed because it is too large Load Diff
+34
View File
@@ -673,6 +673,40 @@ class Account:
"""
return self.__send_content(chat_id, message, reply_to_message_id)
def send_emoji_reaction(self, message_id: str, emoji_shortname: str, chat_id: Optional[str] = None):
"""
Set / unset emoji reaction for a message.
Parameters:
- `message_id`- the `id` of the message
- `emoji_shortname` - the emoji shortname as in Status App
- `chat_id` - the `id` of the chat. If not provided it will be found from `message_id`
"""
if not emoji_shortname.startswith(":"):
emoji_shortname = f":{emoji_shortname}"
if not emoji_shortname.endswith(":"):
emoji_shortname += ":"
emoji_unicode = constants.EMOJI_UNICODES.get(emoji_shortname)
if not emoji_unicode:
raise exceptions.EmojiNotFoundError(emoji_shortname)
if not chat_id:
response = self._call_rpc("messaging", "messageByMessageID", [message_id])
error = response.get("error", {}) or {}
if error:
raise exceptions.ChatNotFoundError(error.get("message"))
chat_id: str = response["result"]["localChatId"]
params = [chat_id, message_id, emoji_unicode]
response = self._call_rpc("messaging", "sendEmojiReaction", params)
error = response.get("error", {}) or {}
if error:
raise exceptions.ChatNotFoundError(error.get("message"))
def __send_content(self, chat_id: str, message: Optional[str] = None, reply_to_message_id: Optional[str] = None, image_path: Optional[str] = None) -> str:
"""
Send a message with optional media attached to the given chat.
+9
View File
@@ -214,6 +214,15 @@ class Channel:
"""
return self.__account.send_image(self.id, file_path, message, reply_to_message_id)
def send_emoji_reaction(self, message_id: str, emoji_shortname: str):
"""
Set / unset emoji reaction for a message in the channel.
Parameters:
- `message_id` - the `id` of the message, as it appears in `self.get_messages()`
- `emoji_shortname` - the emoji shortname as in Status App, with or without the surrounding colons
"""
self.__account.send_emoji_reaction(message_id, emoji_shortname, self.id)
def get_messages(self, 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]:
"""
File diff suppressed because it is too large Load Diff
+7
View File
@@ -74,6 +74,13 @@ class InvalidCommunityChannelColourError(ValueError):
class InvalidCommunityChannelEmojiError(ValueError):
pass
class ChatNotFoundError(Exception):
pass
class EmojiNotFoundError(Exception):
def __init__(self, shortname: str):
super().__init__(f"Emoji shortcode '{shortname}' does not exist...")
class GroupChatCreationError(Exception):
pass
+9
View File
@@ -84,6 +84,15 @@ class GroupChat:
"""
return self.__account.send_image(self.id, file_path, message, reply_to_message_id)
def send_emoji_reaction(self, message_id: str, emoji_shortname: str):
"""
Set / unset emoji reaction for a message in the group chat.
Parameters:
- `message_id` - the `id` of the message, as it appears in `self.get_messages()`
- `emoji_shortname` - the emoji shortname as in Status App, with or without the surrounding colons
"""
self.__account.send_emoji_reaction(message_id, emoji_shortname, self.id)
def get_messages(self, 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]:
"""