mirror of
https://github.com/status-im/status-python-sdk.git
synced 2026-08-30 21:51:14 +00:00
account: mnemonic recovery
Related to https://github.com/status-im/status-bot/issues/7 - Recover account with mnemonic phrase in `login`
This commit is contained in:
+46
-23
@@ -41,6 +41,7 @@ class Account:
|
||||
"initialize": f"{self.http_base_url}InitializeApplication",
|
||||
"login": f"{self.http_base_url}LoginAccount",
|
||||
"create": f"{self.http_base_url}CreateAccountAndLogin",
|
||||
"restore": f"{self.http_base_url}RestoreAccountAndLogin",
|
||||
"logout": f"{self.http_base_url}Logout",
|
||||
"rpc": f"{self.http_base_url}CallRPC"
|
||||
},
|
||||
@@ -49,26 +50,12 @@ class Account:
|
||||
}
|
||||
}
|
||||
self.__signal = Signal(self.urls["socket"]["signals"])
|
||||
response = requests.post(self.urls["http"]["initialize"], json={
|
||||
"dataDir": self.__unix_folder
|
||||
})
|
||||
data: dict = response.json()
|
||||
accounts: list[dict] = data.get("accounts", [])
|
||||
if not isinstance(accounts, list):
|
||||
accounts = []
|
||||
|
||||
self.__available_accounts = [
|
||||
{
|
||||
"display_name": account["name"],
|
||||
"key_uid": account["key-uid"],
|
||||
"created_at": datetime.datetime.fromtimestamp(account["timestamp"])
|
||||
}
|
||||
for account in accounts
|
||||
]
|
||||
# Initialize profile
|
||||
self.available_accounts
|
||||
# In case if there is a hanging logged in session
|
||||
self.logout()
|
||||
|
||||
def login(self, password: str, key_uid: Optional[str] = None, display_name: Optional[str] = None):
|
||||
def login(self, password: str, key_uid: Optional[str] = None, display_name: Optional[str] = None, mnemonic: Optional[str] = None):
|
||||
"""
|
||||
Login to the given account. If it does not exist,
|
||||
it will be created and automatically logged in.
|
||||
@@ -77,13 +64,15 @@ class Account:
|
||||
- `password` - your Status password
|
||||
- `key_uid` - your key unique identifier. If not provided `display_name` will be used to fetch it. This means that each `display_name` can be linked to one `key_uid`
|
||||
- `display_name` - your Status display name. Use `display_name` and `password` parameter combination if you have a 1 to 1 mapping (each display name has a unique `key_uid`)
|
||||
- `mnemonic` - the mnemonic when creating an account. Use this field with `password` and `display_name` to recover an account
|
||||
"""
|
||||
if not key_uid and not display_name:
|
||||
raise ValueError("Please provide either a Key Unique Identifier (key_uid) or a Display Name (display_name)...")
|
||||
|
||||
available_accounts = self.available_accounts
|
||||
# Login combination: display_name + password
|
||||
if not key_uid:
|
||||
for account in self.__available_accounts:
|
||||
for account in available_accounts:
|
||||
if account["display_name"] != display_name:
|
||||
continue
|
||||
|
||||
@@ -91,19 +80,34 @@ class Account:
|
||||
break
|
||||
# Login combination: key_uid + password
|
||||
else:
|
||||
available_key_uids = [current["key_uid"] for current in self.__available_accounts]
|
||||
available_key_uids = [current["key_uid"] for current in available_accounts]
|
||||
if key_uid not in available_key_uids:
|
||||
info = "\n".join([f"{current['key_uid']} - {current['display_name']}" for current in self.__available_accounts])
|
||||
raise ValueError(f"Given Key Unique Identifier is invalid...\nAvailable Key Unique Identifiers:\n{info}")
|
||||
|
||||
is_new_account = isinstance(key_uid, type(None))
|
||||
is_recovery = not isinstance(mnemonic, type(None))
|
||||
|
||||
url_key = "login"
|
||||
params = {
|
||||
"keyUid": key_uid,
|
||||
"password": password,
|
||||
'kdfIterations': self.__kd_iterations
|
||||
}
|
||||
if is_new_account:
|
||||
if is_recovery:
|
||||
self.__validate_display_name(display_name)
|
||||
params = {
|
||||
"mnemonic": mnemonic,
|
||||
"rootDataDir": self.__unix_folder,
|
||||
"kdfIterations": self.__kd_iterations,
|
||||
"displayName": display_name,
|
||||
"password": password,
|
||||
"customizationColor": "primary",
|
||||
"wakuV2LightClient": False,
|
||||
"thirdpartyServicesEnabled": True
|
||||
}
|
||||
url_key = "restore"
|
||||
elif is_new_account:
|
||||
self.__validate_display_name(display_name)
|
||||
params = {
|
||||
"rootDataDir": self.__unix_folder,
|
||||
@@ -114,8 +118,10 @@ class Account:
|
||||
"wakuV2LightClient": False,
|
||||
"thirdpartyServicesEnabled": True,
|
||||
}
|
||||
url_key = "create"
|
||||
|
||||
self.logout()
|
||||
url = self.urls["http"]["login" if not is_new_account else "create"]
|
||||
url = self.urls["http"][url_key]
|
||||
response = requests.post(url, json=params)
|
||||
signal_event = self.__signal.get("node.login")
|
||||
if signal_event["is_error"]:
|
||||
@@ -127,7 +133,7 @@ class Account:
|
||||
"emojis": event["emojiHash"],
|
||||
"key_uid": event["key-uid"],
|
||||
"compressed_key": event["compressedKey"],
|
||||
"mnemonic": event["mnemonic"],
|
||||
"mnemonic": event.get("mnemonic", mnemonic),
|
||||
"display_name": event["display-name"],
|
||||
"bio": event.get("bio", ""),
|
||||
"password": password,
|
||||
@@ -152,7 +158,23 @@ class Account:
|
||||
"""
|
||||
All locally available accounts
|
||||
"""
|
||||
return self.__available_accounts
|
||||
response = requests.post(self.urls["http"]["initialize"], json={
|
||||
"dataDir": self.__unix_folder
|
||||
})
|
||||
data: dict = response.json()
|
||||
accounts: list[dict] = data.get("accounts", [])
|
||||
if not isinstance(accounts, list):
|
||||
accounts = []
|
||||
|
||||
current_available_accounts = [
|
||||
{
|
||||
"display_name": account["name"],
|
||||
"key_uid": account["key-uid"],
|
||||
"created_at": datetime.datetime.fromtimestamp(account["timestamp"])
|
||||
}
|
||||
for account in accounts
|
||||
]
|
||||
return current_available_accounts
|
||||
|
||||
@property
|
||||
def info(self) -> dict:
|
||||
@@ -484,6 +506,7 @@ class Account:
|
||||
if self.__is_messenger_launched:
|
||||
return
|
||||
self.__call_rpc("messaging", "startMessenger")
|
||||
self.__signal.get("wakuv2.peerstats")
|
||||
self.__is_messenger_launched = True
|
||||
|
||||
def __del__(self):
|
||||
|
||||
+23
-2
@@ -42,18 +42,22 @@ If a display name does not follow these rules, a **`ValueError`** will be raised
|
||||
|
||||
## Methods
|
||||
|
||||
### `login(password, key_uid=None, display_name=None)`
|
||||
### `login(password, key_uid=None, display_name=None, mnemonic=None)`
|
||||
|
||||
Login to an existing Status account. If the account does not exist in the initialized data directory, a new account will be created and automatically logged in. After a successful login, the decentralized messenger service is automatically started so the account can send and receive messages.
|
||||
|
||||
An account can also be recovered if the `mnemonic` is passed.
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
|-----|-----|-----|-------------|
|
||||
| `password` | `str` | Yes | Password used to encrypt the account |
|
||||
| `key_uid` | `str` | Yes* | Unique key identifier of the account. If provided, the account will be logged in directly using this identifier. If not provided, then you must use `display_name` and `password` to login. |
|
||||
| `display_name` | `str` | Yes* | Display name of the account. Used to resolve the `key_uid` if it is not provided, or to create a new account if one does not already exist. |
|
||||
| `display_name` | `str` | Yes* | Display name of the account. Used to resolve the `key_uid` if it is not provided, or to create a new account if one does not already exist. This field is required if an account needs to be recovered with `mnemonic`. |
|
||||
| `mnemonic` | `str` | No | The mnemonic from [`info`](./account.md#info). Use this field with `password` and `display_name` to recover the account.<br>**Note**: You can pass a different `display_name` but that will be internal only. When an account is recovered setting [`display_name`](./account.md#display_name) can be buggy. Ideally when recovering the account, use the original `display_name` of the account. |
|
||||
|
||||
Returns the current `Account` instance, allowing method chaining.
|
||||
|
||||
Login with `display_name`:
|
||||
```python
|
||||
from bot import Account
|
||||
|
||||
@@ -65,6 +69,9 @@ params = {
|
||||
account.login(**params)
|
||||
```
|
||||
|
||||
**Note**: This assumes that `display_name` and is unique for every `key_uid`. If there are duplicated `display_names` then the first found match will be used. You can log in with `key_uid` if you have `display_name` duplicates.
|
||||
|
||||
Login with `key_uid`:
|
||||
```python
|
||||
from bot import Account
|
||||
|
||||
@@ -76,6 +83,20 @@ params = {
|
||||
account.login(**params)
|
||||
```
|
||||
|
||||
Recover account:
|
||||
|
||||
```python
|
||||
from bot import Account
|
||||
|
||||
account = Account()
|
||||
params = {
|
||||
"display_name": "status-app-bot",
|
||||
"password": "SNTPUMP",
|
||||
"mnemonic" : "phrase_1 phrase_2 phrase_3 phrase_4 phrase_5 phrase_6 phrase_7 phrase_8 phrase_9 phrase_10 phrase_11 phrase_12"
|
||||
}
|
||||
account.login(**params)
|
||||
```
|
||||
|
||||
### `logout()`
|
||||
|
||||
Logout from the currently logged-in Status account. This method also clears the internal account state and stops the active messenger session. This function is also supported in `del` and when the script automatically finishes.
|
||||
|
||||
Reference in New Issue
Block a user