mirror of
https://github.com/status-im/status-python-sdk.git
synced 2026-08-30 21:51:14 +00:00
bot: Add Profile Picture
- Set local profile picture - Documentation - Monitoring implementation
This commit is contained in:
committed by
Nick Ninov
parent
e3af713e20
commit
a665fb7d46
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
+76
-2
@@ -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]:
|
||||
"""
|
||||
|
||||
@@ -2,3 +2,4 @@ requests
|
||||
websocket-client
|
||||
websockets
|
||||
pandas
|
||||
pillow
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -733,6 +733,47 @@ account.login(**params)
|
||||
del account.bio
|
||||
```
|
||||
|
||||
### `profile_picture`
|
||||
|
||||
Get or update the **profile picture** of the currently logged‑in 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 logged‑in session**. Attempting to use signals before calling `login()` will raise an exception. Signals are low‑level events emitted by the Status Backend. Examples include:
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user