htcli 1.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- htcli-1.1.0.dist-info/METADATA +509 -0
- htcli-1.1.0.dist-info/RECORD +140 -0
- htcli-1.1.0.dist-info/WHEEL +4 -0
- htcli-1.1.0.dist-info/entry_points.txt +2 -0
- htcli-1.1.0.dist-info/licenses/LICENSE +21 -0
- src/__init__.py +0 -0
- src/htcli/__init__.py +5 -0
- src/htcli/client/__init__.py +338 -0
- src/htcli/client/extrinsics/__init__.py +26 -0
- src/htcli/client/extrinsics/base.py +487 -0
- src/htcli/client/extrinsics/consensus.py +79 -0
- src/htcli/client/extrinsics/governance.py +714 -0
- src/htcli/client/extrinsics/identity.py +490 -0
- src/htcli/client/extrinsics/node.py +1054 -0
- src/htcli/client/extrinsics/overwatch.py +401 -0
- src/htcli/client/extrinsics/staking.py +1504 -0
- src/htcli/client/extrinsics/subnet.py +2218 -0
- src/htcli/client/extrinsics/validator.py +203 -0
- src/htcli/client/extrinsics/wallet.py +323 -0
- src/htcli/client/offchain/__init__.py +10 -0
- src/htcli/client/offchain/backup.py +385 -0
- src/htcli/client/offchain/config.py +541 -0
- src/htcli/client/offchain/wallet.py +839 -0
- src/htcli/client/rpc/__init__.py +20 -0
- src/htcli/client/rpc/chain.py +568 -0
- src/htcli/client/rpc/node.py +783 -0
- src/htcli/client/rpc/overwatch.py +680 -0
- src/htcli/client/rpc/staking.py +216 -0
- src/htcli/client/rpc/subnet.py +2104 -0
- src/htcli/client/rpc/wallet.py +912 -0
- src/htcli/commands/__init__.py +31 -0
- src/htcli/commands/chain/__init__.py +66 -0
- src/htcli/commands/chain/display.py +204 -0
- src/htcli/commands/chain/handlers.py +260 -0
- src/htcli/commands/config/__init__.py +158 -0
- src/htcli/commands/config/display.py +353 -0
- src/htcli/commands/config/handlers.py +347 -0
- src/htcli/commands/config/prompts.py +357 -0
- src/htcli/commands/consensus/__init__.py +61 -0
- src/htcli/commands/consensus/handlers.py +100 -0
- src/htcli/commands/governance/__init__.py +49 -0
- src/htcli/commands/governance/handlers.py +81 -0
- src/htcli/commands/node/__init__.py +304 -0
- src/htcli/commands/node/display.py +749 -0
- src/htcli/commands/node/error_handling.py +470 -0
- src/htcli/commands/node/handlers.py +844 -0
- src/htcli/commands/node/prompts.py +346 -0
- src/htcli/commands/overwatch/__init__.py +219 -0
- src/htcli/commands/overwatch/display.py +396 -0
- src/htcli/commands/overwatch/error_handling.py +276 -0
- src/htcli/commands/overwatch/handlers.py +443 -0
- src/htcli/commands/overwatch/prompts.py +359 -0
- src/htcli/commands/stake/__init__.py +736 -0
- src/htcli/commands/stake/display.py +1103 -0
- src/htcli/commands/stake/error_handling.py +425 -0
- src/htcli/commands/stake/handlers.py +1902 -0
- src/htcli/commands/stake/prompts.py +1080 -0
- src/htcli/commands/subnet/__init__.py +639 -0
- src/htcli/commands/subnet/display.py +801 -0
- src/htcli/commands/subnet/error_handling.py +524 -0
- src/htcli/commands/subnet/handlers.py +2855 -0
- src/htcli/commands/subnet/prompts.py +1225 -0
- src/htcli/commands/validator/__init__.py +192 -0
- src/htcli/commands/validator/display.py +54 -0
- src/htcli/commands/validator/handlers.py +340 -0
- src/htcli/commands/wallet/__init__.py +546 -0
- src/htcli/commands/wallet/display.py +806 -0
- src/htcli/commands/wallet/error_handling.py +210 -0
- src/htcli/commands/wallet/handlers.py +3040 -0
- src/htcli/commands/wallet/prompts.py +1518 -0
- src/htcli/config.py +184 -0
- src/htcli/dependencies.py +186 -0
- src/htcli/errors/__init__.py +63 -0
- src/htcli/errors/base.py +141 -0
- src/htcli/errors/display.py +20 -0
- src/htcli/errors/handlers.py +710 -0
- src/htcli/main.py +343 -0
- src/htcli/models/__init__.py +21 -0
- src/htcli/models/enums/enum_types.py +35 -0
- src/htcli/models/errors.py +103 -0
- src/htcli/models/requests/__init__.py +197 -0
- src/htcli/models/requests/config.py +70 -0
- src/htcli/models/requests/consensus.py +19 -0
- src/htcli/models/requests/governance.py +38 -0
- src/htcli/models/requests/identity.py +51 -0
- src/htcli/models/requests/key.py +22 -0
- src/htcli/models/requests/node.py +91 -0
- src/htcli/models/requests/overwatch.py +64 -0
- src/htcli/models/requests/staking.py +580 -0
- src/htcli/models/requests/subnet.py +195 -0
- src/htcli/models/requests/validator.py +139 -0
- src/htcli/models/requests/wallet.py +118 -0
- src/htcli/models/responses/__init__.py +147 -0
- src/htcli/models/responses/base.py +18 -0
- src/htcli/models/responses/chain.py +39 -0
- src/htcli/models/responses/config.py +58 -0
- src/htcli/models/responses/identity.py +102 -0
- src/htcli/models/responses/overwatch.py +51 -0
- src/htcli/models/responses/staking.py +502 -0
- src/htcli/models/responses/subnet.py +856 -0
- src/htcli/models/responses/wallet.py +185 -0
- src/htcli/ui/__init__.py +87 -0
- src/htcli/ui/colors.py +309 -0
- src/htcli/ui/components/__init__.py +60 -0
- src/htcli/ui/components/panels.py +174 -0
- src/htcli/ui/components/progress.py +166 -0
- src/htcli/ui/components/spinners.py +92 -0
- src/htcli/ui/components/tables.py +809 -0
- src/htcli/ui/components/trees.py +721 -0
- src/htcli/ui/display.py +336 -0
- src/htcli/ui/prompts.py +870 -0
- src/htcli/utils/__init__.py +76 -0
- src/htcli/utils/blockchain/__init__.py +75 -0
- src/htcli/utils/blockchain/formatting.py +368 -0
- src/htcli/utils/blockchain/patches.py +286 -0
- src/htcli/utils/blockchain/peer_id.py +186 -0
- src/htcli/utils/blockchain/staking.py +448 -0
- src/htcli/utils/blockchain/type_registry.py +1373 -0
- src/htcli/utils/blockchain/validation.py +179 -0
- src/htcli/utils/cache.py +613 -0
- src/htcli/utils/constants.py +38 -0
- src/htcli/utils/legacy/__init__.py +12 -0
- src/htcli/utils/legacy/colors.py +311 -0
- src/htcli/utils/legacy/crypto.py +1176 -0
- src/htcli/utils/legacy/formatting.py +452 -0
- src/htcli/utils/legacy/interactive.py +306 -0
- src/htcli/utils/legacy/subnet_manifest.py +265 -0
- src/htcli/utils/legacy/validation.py +488 -0
- src/htcli/utils/logging.py +183 -0
- src/htcli/utils/network/__init__.py +20 -0
- src/htcli/utils/network/subnet.py +344 -0
- src/htcli/utils/prompts.py +27 -0
- src/htcli/utils/scale_codec.py +155 -0
- src/htcli/utils/validation/__init__.py +57 -0
- src/htcli/utils/validation/prompt_validators.py +267 -0
- src/htcli/utils/wallet/__init__.py +65 -0
- src/htcli/utils/wallet/auth.py +151 -0
- src/htcli/utils/wallet/core.py +1069 -0
- src/htcli/utils/wallet/crypto.py +1615 -0
- src/htcli/utils/wallet/migration.py +159 -0
src/htcli/ui/display.py
ADDED
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
"""
|
|
2
|
+
HTCLI display utilities and console management.
|
|
3
|
+
Provides consistent display functions across the CLI.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from typing import Any, Union
|
|
7
|
+
|
|
8
|
+
from rich.align import Align
|
|
9
|
+
from rich.console import Console
|
|
10
|
+
from rich.panel import Panel
|
|
11
|
+
from rich.rule import Rule
|
|
12
|
+
from rich.text import Text
|
|
13
|
+
|
|
14
|
+
from .colors import HTCLIColors, HTCLITheme
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class HTCLIConsole:
|
|
18
|
+
"""Enhanced console with HTCLI theming and utilities."""
|
|
19
|
+
|
|
20
|
+
def __init__(self, force_terminal: bool = None):
|
|
21
|
+
# If force_terminal is None, let Rich auto-detect
|
|
22
|
+
# But ensure we have proper terminal capabilities for Live displays
|
|
23
|
+
import sys
|
|
24
|
+
# Check if stdout is a TTY (terminal) - important for Live displays
|
|
25
|
+
is_terminal = sys.stdout.isatty() if force_terminal is None else force_terminal
|
|
26
|
+
|
|
27
|
+
self.console = Console(
|
|
28
|
+
theme=HTCLITheme.get_theme(),
|
|
29
|
+
force_terminal=is_terminal,
|
|
30
|
+
width=120, # Consistent width for better formatting
|
|
31
|
+
# Ensure proper terminal detection for live updates
|
|
32
|
+
legacy_windows=False, # Use modern terminal features
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
def print(self, *args, **kwargs):
|
|
36
|
+
"""Print with HTCLI theming."""
|
|
37
|
+
self.console.print(*args, **kwargs)
|
|
38
|
+
|
|
39
|
+
def print_panel(
|
|
40
|
+
self,
|
|
41
|
+
content: Any,
|
|
42
|
+
title: str = None,
|
|
43
|
+
title_style: str = "htcli.panel.title",
|
|
44
|
+
border_style: str = "htcli.border",
|
|
45
|
+
padding: tuple = (1, 2),
|
|
46
|
+
expand: bool = False,
|
|
47
|
+
):
|
|
48
|
+
"""Print content in a styled panel."""
|
|
49
|
+
panel = Panel(
|
|
50
|
+
content,
|
|
51
|
+
title=title,
|
|
52
|
+
title_align="left",
|
|
53
|
+
border_style=border_style,
|
|
54
|
+
padding=padding,
|
|
55
|
+
expand=expand,
|
|
56
|
+
)
|
|
57
|
+
if title:
|
|
58
|
+
panel.title = Text(title, style=title_style)
|
|
59
|
+
self.console.print(panel)
|
|
60
|
+
|
|
61
|
+
def print_rule(
|
|
62
|
+
self, title: str = None, style: str = "htcli.border", align: str = "center"
|
|
63
|
+
):
|
|
64
|
+
"""Print a horizontal rule with optional title."""
|
|
65
|
+
if title:
|
|
66
|
+
rule = Rule(Text(title, style="htcli.title"), style=style, align=align)
|
|
67
|
+
else:
|
|
68
|
+
rule = Rule(style=style)
|
|
69
|
+
self.console.print(rule)
|
|
70
|
+
|
|
71
|
+
def clear(self):
|
|
72
|
+
"""Clear the console."""
|
|
73
|
+
self.console.clear()
|
|
74
|
+
|
|
75
|
+
def input(self, prompt: str = "", style: str = "htcli.prompt") -> str:
|
|
76
|
+
"""Get input with styled prompt."""
|
|
77
|
+
styled_prompt = Text(prompt, style=style)
|
|
78
|
+
return self.console.input(styled_prompt)
|
|
79
|
+
|
|
80
|
+
def print_json(
|
|
81
|
+
self, data: Any = None, *, indent: int = 2, sort_keys: bool = False
|
|
82
|
+
) -> None:
|
|
83
|
+
"""Proxy to rich Console.print_json for JSON rendering."""
|
|
84
|
+
self.console.print_json(data=data, indent=indent, sort_keys=sort_keys)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
# Global console instance
|
|
88
|
+
_console = HTCLIConsole()
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def get_console() -> HTCLIConsole:
|
|
92
|
+
"""Get the global HTCLI console instance."""
|
|
93
|
+
return _console
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def print_success(
|
|
97
|
+
message: str, title: str = None, emoji: bool = True, panel: bool = False
|
|
98
|
+
):
|
|
99
|
+
"""Print a success message."""
|
|
100
|
+
prefix = "✅ " if emoji else ""
|
|
101
|
+
text = f"{prefix}{message}"
|
|
102
|
+
|
|
103
|
+
if panel:
|
|
104
|
+
_console.print_panel(
|
|
105
|
+
Text(text, style="htcli.status.success"),
|
|
106
|
+
title=title,
|
|
107
|
+
border_style="success",
|
|
108
|
+
)
|
|
109
|
+
else:
|
|
110
|
+
_console.print(f"[htcli.status.success]{text}[/]")
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def print_error(
|
|
114
|
+
message: str,
|
|
115
|
+
title: str = None,
|
|
116
|
+
emoji: bool = True,
|
|
117
|
+
panel: bool = False,
|
|
118
|
+
suggestions: list[str] = None,
|
|
119
|
+
):
|
|
120
|
+
"""Print an error message with optional suggestions."""
|
|
121
|
+
prefix = "❌ " if emoji else ""
|
|
122
|
+
text = f"{prefix}{message}"
|
|
123
|
+
|
|
124
|
+
content = Text(text, style="htcli.status.error")
|
|
125
|
+
|
|
126
|
+
if suggestions:
|
|
127
|
+
content.append("\n\n")
|
|
128
|
+
content.append("💡 Suggestions:\n", style="htcli.warning")
|
|
129
|
+
for suggestion in suggestions:
|
|
130
|
+
content.append(f"• {suggestion}\n", style="htcli.info")
|
|
131
|
+
|
|
132
|
+
if panel:
|
|
133
|
+
_console.print_panel(content, title=title, border_style="error")
|
|
134
|
+
else:
|
|
135
|
+
_console.print(content)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def print_warning(
|
|
139
|
+
message: str, title: str = None, emoji: bool = True, panel: bool = False
|
|
140
|
+
):
|
|
141
|
+
"""Print a warning message."""
|
|
142
|
+
prefix = "⚠️ " if emoji else "" # Two spaces after emoji for proper spacing
|
|
143
|
+
text = f"{prefix}{message}"
|
|
144
|
+
|
|
145
|
+
if panel:
|
|
146
|
+
_console.print_panel(
|
|
147
|
+
Text(text, style="htcli.status.pending"),
|
|
148
|
+
title=title,
|
|
149
|
+
border_style="warning",
|
|
150
|
+
)
|
|
151
|
+
else:
|
|
152
|
+
_console.print(f"[htcli.status.pending]{text}[/]")
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def print_info(
|
|
156
|
+
message: str, title: str = None, emoji: bool = True, panel: bool = False
|
|
157
|
+
):
|
|
158
|
+
"""Print an info message."""
|
|
159
|
+
prefix = "ℹ️ " if emoji else "" # Add space after emoji
|
|
160
|
+
text = f"{prefix}{message}"
|
|
161
|
+
|
|
162
|
+
if panel:
|
|
163
|
+
_console.print_panel(
|
|
164
|
+
Text(text, style="htcli.info"), title=title, border_style="info"
|
|
165
|
+
)
|
|
166
|
+
else:
|
|
167
|
+
_console.print(f"[htcli.info]{text}[/]")
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def print_header(
|
|
171
|
+
title: str, subtitle: str = None, version: str = None, width: int = 80
|
|
172
|
+
):
|
|
173
|
+
"""Print a styled header for commands."""
|
|
174
|
+
# Create the main title
|
|
175
|
+
title_text = Text(title, style="htcli.header")
|
|
176
|
+
if version:
|
|
177
|
+
title_text.append(f" v{version}", style="htcli.subtitle")
|
|
178
|
+
|
|
179
|
+
header_content = Align.center(title_text)
|
|
180
|
+
|
|
181
|
+
if subtitle:
|
|
182
|
+
subtitle_text = Text(subtitle, style="htcli.subtitle")
|
|
183
|
+
header_content = Align.center(f"{title_text}\n{subtitle_text}")
|
|
184
|
+
|
|
185
|
+
_console.print_panel(
|
|
186
|
+
header_content, border_style="htcli.primary", padding=(1, 4), expand=False
|
|
187
|
+
)
|
|
188
|
+
_console.print()
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def print_footer(message: str = None, show_help: bool = True, command: str = None):
|
|
192
|
+
"""Print a styled footer."""
|
|
193
|
+
if not message and not show_help:
|
|
194
|
+
return
|
|
195
|
+
|
|
196
|
+
content = []
|
|
197
|
+
|
|
198
|
+
if message:
|
|
199
|
+
content.append(Text(message, style="htcli.subtitle"))
|
|
200
|
+
|
|
201
|
+
if show_help:
|
|
202
|
+
help_text = "Use --help for more information"
|
|
203
|
+
if command:
|
|
204
|
+
help_text = f"Use 'htcli {command} --help' for more information"
|
|
205
|
+
content.append(Text(help_text, style="htcli.muted"))
|
|
206
|
+
|
|
207
|
+
if content:
|
|
208
|
+
_console.print()
|
|
209
|
+
for item in content:
|
|
210
|
+
_console.print(Align.center(item))
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def print_status(status: str, message: str, emoji_map: dict = None):
|
|
214
|
+
"""Print a status message with appropriate styling."""
|
|
215
|
+
default_emojis = {
|
|
216
|
+
"success": "✅",
|
|
217
|
+
"error": "❌",
|
|
218
|
+
"warning": "⚠️",
|
|
219
|
+
"info": "ℹ️",
|
|
220
|
+
"loading": "⏳",
|
|
221
|
+
"active": "🟢",
|
|
222
|
+
"inactive": "⚪",
|
|
223
|
+
"pending": "🟡",
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
emojis = emoji_map or default_emojis
|
|
227
|
+
emoji = emojis.get(status.lower(), "•")
|
|
228
|
+
color = HTCLIColors.get_status_color(status)
|
|
229
|
+
|
|
230
|
+
_console.print(f"[{color}]{emoji} {message}[/]")
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def print_blockchain_data(title: str, data: dict, highlight_keys: list[str] = None):
|
|
234
|
+
"""Print blockchain data in a formatted way."""
|
|
235
|
+
highlight_keys = highlight_keys or []
|
|
236
|
+
|
|
237
|
+
content = []
|
|
238
|
+
for key, value in data.items():
|
|
239
|
+
key_style = "htcli.highlight" if key in highlight_keys else "htcli.subtitle"
|
|
240
|
+
|
|
241
|
+
# Format different types of blockchain data
|
|
242
|
+
if key.lower() in ["address", "account", "validator"]:
|
|
243
|
+
formatted_value = f"[htcli.address]{value}[/]"
|
|
244
|
+
elif key.lower() in ["amount", "balance", "stake", "reward"]:
|
|
245
|
+
formatted_value = f"[htcli.amount]{value}[/]"
|
|
246
|
+
elif key.lower() in ["hash", "block_hash", "tx_hash"]:
|
|
247
|
+
formatted_value = f"[htcli.hash]{value}[/]"
|
|
248
|
+
elif key.lower() in ["status", "state"]:
|
|
249
|
+
formatted_value = f"[htcli.status.{value.lower()}]{value}[/]"
|
|
250
|
+
else:
|
|
251
|
+
formatted_value = f"[htcli.value]{value}[/]"
|
|
252
|
+
|
|
253
|
+
content.append(f"[{key_style}]{key}:[/] {formatted_value}")
|
|
254
|
+
|
|
255
|
+
_console.print_panel("\n".join(content), title=title, border_style="htcli.border")
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
def print_command_result(
|
|
259
|
+
success: bool,
|
|
260
|
+
message: str,
|
|
261
|
+
data: dict = None,
|
|
262
|
+
transaction_hash: str = None,
|
|
263
|
+
suggestions: list[str] = None,
|
|
264
|
+
):
|
|
265
|
+
"""Print standardized command result."""
|
|
266
|
+
if success:
|
|
267
|
+
print_success(message, panel=True)
|
|
268
|
+
if transaction_hash:
|
|
269
|
+
_console.print(
|
|
270
|
+
f"\n[htcli.subtitle]Transaction Hash:[/] [htcli.hash]{transaction_hash}[/]"
|
|
271
|
+
)
|
|
272
|
+
if data:
|
|
273
|
+
print_blockchain_data("Result Details", data)
|
|
274
|
+
else:
|
|
275
|
+
print_error(message, panel=True, suggestions=suggestions)
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
def print_separator(char: str = "─", style: str = "htcli.border"):
|
|
279
|
+
"""Print a separator line."""
|
|
280
|
+
_console.print(Rule(style=style, characters=char))
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def print_logo():
|
|
284
|
+
"""Print the HTCLI logo."""
|
|
285
|
+
logo = """
|
|
286
|
+
██╗ ██╗████████╗ ██████╗██╗ ██╗
|
|
287
|
+
██║ ██║╚══██╔══╝██╔════╝██║ ██║
|
|
288
|
+
███████║ ██║ ██║ ██║ ██║
|
|
289
|
+
██╔══██║ ██║ ██║ ██║ ██║
|
|
290
|
+
██║ ██║ ██║ ╚██████╗███████╗██║
|
|
291
|
+
╚═╝ ╚═╝ ╚═╝ ╚═════╝╚══════╝╚═╝
|
|
292
|
+
|
|
293
|
+
Hypertensor Command Line Interface
|
|
294
|
+
"""
|
|
295
|
+
|
|
296
|
+
_console.print(Align.center(Text(logo, style="htcli.primary", justify="center")))
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
# Utility functions for quick formatting
|
|
300
|
+
def format_address(address: str, truncate: bool = True) -> str:
|
|
301
|
+
"""Format a blockchain address with truncation."""
|
|
302
|
+
if truncate and len(address) > 20:
|
|
303
|
+
return f"{address[:8]}...{address[-8:]}"
|
|
304
|
+
return address
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
def format_amount(
|
|
308
|
+
amount: Union[int, float], decimals: int = 6, currency: str = "TENSOR"
|
|
309
|
+
) -> str:
|
|
310
|
+
"""Format an amount with proper decimals and currency."""
|
|
311
|
+
if isinstance(amount, int):
|
|
312
|
+
formatted = f"{amount / (10**18):.{decimals}f}"
|
|
313
|
+
else:
|
|
314
|
+
formatted = f"{amount:.{decimals}f}"
|
|
315
|
+
return f"{formatted} {currency}"
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
def format_hash(hash_value: str, truncate: bool = True) -> str:
|
|
319
|
+
"""Format a hash value with optional truncation."""
|
|
320
|
+
if truncate and len(hash_value) > 16:
|
|
321
|
+
return f"{hash_value[:8]}...{hash_value[-8:]}"
|
|
322
|
+
return hash_value
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
def format_status(status: str) -> str:
|
|
326
|
+
"""Format a status with appropriate emoji."""
|
|
327
|
+
status_emojis = {
|
|
328
|
+
"active": "🟢 Active",
|
|
329
|
+
"inactive": "⚪ Inactive",
|
|
330
|
+
"pending": "🟡 Pending",
|
|
331
|
+
"success": "✅ Success",
|
|
332
|
+
"failed": "❌ Failed",
|
|
333
|
+
"error": "❌ Error",
|
|
334
|
+
"warning": "⚠️ Warning",
|
|
335
|
+
}
|
|
336
|
+
return status_emojis.get(status.lower(), f"• {status}")
|