bot: Add Profile Picture

- Set local profile picture
- Documentation
- Monitoring implementation
This commit is contained in:
Siddarth Kumar
2026-05-10 22:07:35 +03:00
committed by Nick Ninov
parent e3af713e20
commit a665fb7d46
6 changed files with 120 additions and 2 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

+76 -2
View File
@@ -1,8 +1,11 @@
from typing import Optional, Union, Generator, Any
import requests, datetime, re, logging, os, json, ast
import requests, datetime, re, logging, os, json, ast, shutil
import pandas as pd
from PIL import Image
from PIL.JpegImagePlugin import JpegImageFile
from .signal import Signal
from .logger import Logger
class Account:
# Enum mappings from original wakuext.py
@@ -19,7 +22,8 @@ class Account:
"messaging": "wakuext",
"urls": "sharedurls",
"wallets": "wallet",
"account": "accounts"
"account": "accounts",
"identity": "multiaccounts"
}
def __init__(self, domain: str = "localhost", port: int = 8080, is_secure: bool = False):
"""
@@ -38,6 +42,13 @@ class Account:
# NOTE: This might change for initial release
self.__backup_local_folder = os.path.join(os.path.dirname(os.path.dirname(__file__)), "backups")
os.makedirs(self.__backup_local_folder, exist_ok=True)
# Path of where images will be uploaded to Status Backend
self.__docker_asset_folder = "./assets"
# As the docker-compose.yaml folder is at the moment
# NOTE: This might change for initial release
self.__assets_local_folder = os.path.join(os.path.dirname(os.path.dirname(__file__)), "assets")
os.makedirs(self.__assets_local_folder, exist_ok=True)
self.__logger = Logger()
self.__timestamp_divisor = 1_000
self.__kd_iterations = 256000
@@ -284,6 +295,69 @@ class Account:
def bio(self):
self.bio = ""
@property
def profile_picture(self) -> Optional[JpegImageFile]:
"""
Get current profile picture
"""
files = [
os.path.join(self.__assets_local_folder, f)
for f in os.listdir(self.__assets_local_folder)
if os.path.isfile(os.path.join(self.__assets_local_folder, f))
]
if not files:
return
latest_file_path = max(files, key=os.path.getctime)
for file in files:
if file == latest_file_path:
continue
os.remove(file)
return Image.open(latest_file_path)
@profile_picture.setter
def profile_picture(self, file_path: str):
if not isinstance(file_path, str):
return
if not os.path.exists(file_path):
raise Exception(f"File path {file_path} does not exist")
suffix = (".jpg", ".png", ".jpeg")
if not file_path.endswith(suffix):
raise Exception(f"Image must be one of the following extensions: {suffix}")
file_name = os.path.basename(file_path)
extension = file_name.split(".")[-1]
asset_file_name = f"profile.{extension}"
asset_file_path = os.path.join(self.__assets_local_folder, asset_file_name)
docker_file_path = self.__docker_asset_folder + "/" + asset_file_name
for file_name in os.listdir(self.__assets_local_folder):
current_file_path = os.path.join(self.__assets_local_folder, file_name)
if not os.path.isfile(current_file_path) or current_file_path == file_path:
continue
os.remove(current_file_path)
try:
shutil.copy(file_path, asset_file_path)
except shutil.SameFileError:
self.logger.info("File is already in asset path")
img = Image.open(asset_file_path)
params = [
self.info["key_uid"],
docker_file_path,
0,
0,
*img.size
]
self.logger.info(f"Setting {file_path} as profile picture")
self.__call_rpc("identity", "storeIdentityImage", params)
self.logger.info(f"Profile picture has been updated!")
@property
def contacts(self) -> dict[str, dict]:
"""
+1
View File
@@ -2,3 +2,4 @@ requests
websocket-client
websockets
pandas
pillow
+1
View File
@@ -14,6 +14,7 @@ services:
command: '-address 0.0.0.0:8080'
volumes:
- ./backups:/root/.config/Status/backups
- ./assets:/assets:ro
networks:
- status-bridge
healthcheck:
+41
View File
@@ -733,6 +733,47 @@ account.login(**params)
del account.bio
```
### `profile_picture`
Get or update the **profile picture** of the currently loggedin account. The image is the same one shown on the user's profile in Status App.
Returns `PIL.Image.Image` when reading the property, or `None` if no profile picture has been set.
```python
from bot import Account
account = Account()
params = {
"display_name": "status-app-bot",
"password": "SNTPUMP"
}
account.login(**params)
# Read the current profile picture
image = account.profile_picture
if image:
image.show()
```
The file path assigned to `profile_picture` will be automatically set as the latest profile picture in Status App. If the given file does not exist or the extension is not supported, an **exception will be raised**. Supported image formats are `.jpg`, `.jpeg` and `.png`.
```python
from bot import Account
account = Account()
params = {
"display_name": "status-app-bot",
"password": "SNTPUMP"
}
account.login(**params)
# Update the profile picture
account.profile_picture = "./full_path/to/my_image.png"
account.profile_picture.show()
```
When a new profile picture is set, any previous image in the **assets** folder is removed. The image is also copied into the Status Backend Docker volume so it is picked up by the backend when updating the account identity.
### `signal`
The property exists in `Account` because signals require an **active loggedin session**. Attempting to use signals before calling `login()` will raise an exception. Signals are lowlevel events emitted by the Status Backend. Examples include:
+1
View File
@@ -184,6 +184,7 @@ def create_bot(config: dict) -> Account:
if account.info["compressed_key"] != config["bot"]["compressed_key"]:
raise Exception("Target compressed key and logged in compressed key are different")
account.profile_picture = os.path.join(os.path.dirname(__file__), "assets", "profile.jpg")
account.logger.info(f"Account Information:\nCompressed Key: {account.info['compressed_key']}\nPublic Key: {account.info['public_key']}\nURL: {account.info['url']}")
return account