my-cli-utilities 0.1.0__tar.gz
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.
- my_cli_utilities-0.1.0/LICENSE +21 -0
- my_cli_utilities-0.1.0/PKG-INFO +82 -0
- my_cli_utilities-0.1.0/README.md +61 -0
- my_cli_utilities-0.1.0/account_pool_cli/__init__.py +1 -0
- my_cli_utilities-0.1.0/account_pool_cli/cli.py +226 -0
- my_cli_utilities-0.1.0/device_spy_cli/__init__.py +1 -0
- my_cli_utilities-0.1.0/device_spy_cli/cli.py +165 -0
- my_cli_utilities-0.1.0/my_cli_utilities.egg-info/PKG-INFO +82 -0
- my_cli_utilities-0.1.0/my_cli_utilities.egg-info/SOURCES.txt +13 -0
- my_cli_utilities-0.1.0/my_cli_utilities.egg-info/dependency_links.txt +1 -0
- my_cli_utilities-0.1.0/my_cli_utilities.egg-info/entry_points.txt +3 -0
- my_cli_utilities-0.1.0/my_cli_utilities.egg-info/requires.txt +3 -0
- my_cli_utilities-0.1.0/my_cli_utilities.egg-info/top_level.txt +2 -0
- my_cli_utilities-0.1.0/pyproject.toml +38 -0
- my_cli_utilities-0.1.0/setup.cfg +4 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) [year] [fullname]
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: my-cli-utilities
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A suite of CLI utilities for account pool and device spy services.
|
|
5
|
+
Author-email: Swain Zheng <swain.zheng@xxxx.com>
|
|
6
|
+
License: MIT License
|
|
7
|
+
Project-URL: Homepage, https://github.com/yourusername/my-cli-utilities
|
|
8
|
+
Project-URL: Bug Tracker, https://github.com/yourusername/my-cli-utilities/issues
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Topic :: Utilities
|
|
14
|
+
Requires-Python: >=3.8
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
License-File: LICENSE
|
|
17
|
+
Requires-Dist: httpx>=0.20
|
|
18
|
+
Requires-Dist: fire>=0.4
|
|
19
|
+
Requires-Dist: requests>=2.20
|
|
20
|
+
Dynamic: license-file
|
|
21
|
+
|
|
22
|
+
# Account Pool CLI
|
|
23
|
+
|
|
24
|
+
A simple CLI tool to interact with the account pool service, allowing you to fetch account details.
|
|
25
|
+
|
|
26
|
+
## Installation
|
|
27
|
+
|
|
28
|
+
To install the Account Pool CLI, run the following command in your terminal:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pip install .
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
(If you publish it to PyPI, you would use `pip install account-pool-cli`)
|
|
35
|
+
|
|
36
|
+
## Usage
|
|
37
|
+
|
|
38
|
+
Once installed, you can use the `ap` command:
|
|
39
|
+
|
|
40
|
+
### Get account info by main number
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
ap info <main_number> [--env_name <environment_name>]
|
|
44
|
+
```
|
|
45
|
+
Example:
|
|
46
|
+
```bash
|
|
47
|
+
ap info 1234567890
|
|
48
|
+
ap info +1234567890 --env_name specific_env
|
|
49
|
+
```
|
|
50
|
+
`env_name` defaults to `webaqaxmn` if not provided.
|
|
51
|
+
|
|
52
|
+
### Get a random account
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
ap get_random_account <account_type> [--env_name <environment_name>]
|
|
56
|
+
```
|
|
57
|
+
Example:
|
|
58
|
+
```bash
|
|
59
|
+
ap get_random_account "QQ"
|
|
60
|
+
ap get_random_account "YOUR_ACCOUNT_TYPE" --env_name specific_env
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Get account info by ID
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
ap get_account_by_id <account_id> [--env_name <environment_name>]
|
|
67
|
+
```
|
|
68
|
+
Example:
|
|
69
|
+
```bash
|
|
70
|
+
ap get_account_by_id "60c72b2f9b1d8f001f8e4d3a"
|
|
71
|
+
ap get_account_by_id "60c72b2f9b1d8f001f8e4d3b" --env_name specific_env
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Development
|
|
75
|
+
|
|
76
|
+
To set up for development:
|
|
77
|
+
|
|
78
|
+
1. Clone the repository.
|
|
79
|
+
2. Create a virtual environment: `python -m venv .venv`
|
|
80
|
+
3. Activate it: `source .venv/bin/activate` (on Linux/macOS) or `.venv\Scripts\activate` (on Windows)
|
|
81
|
+
4. Install dependencies: `pip install -r requirements-dev.txt` (You'll need to create this file if you have dev-specific dependencies like `pytest`)
|
|
82
|
+
5. Install the package in editable mode: `pip install -e .`
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Account Pool CLI
|
|
2
|
+
|
|
3
|
+
A simple CLI tool to interact with the account pool service, allowing you to fetch account details.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
To install the Account Pool CLI, run the following command in your terminal:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install .
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
(If you publish it to PyPI, you would use `pip install account-pool-cli`)
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
Once installed, you can use the `ap` command:
|
|
18
|
+
|
|
19
|
+
### Get account info by main number
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
ap info <main_number> [--env_name <environment_name>]
|
|
23
|
+
```
|
|
24
|
+
Example:
|
|
25
|
+
```bash
|
|
26
|
+
ap info 1234567890
|
|
27
|
+
ap info +1234567890 --env_name specific_env
|
|
28
|
+
```
|
|
29
|
+
`env_name` defaults to `webaqaxmn` if not provided.
|
|
30
|
+
|
|
31
|
+
### Get a random account
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
ap get_random_account <account_type> [--env_name <environment_name>]
|
|
35
|
+
```
|
|
36
|
+
Example:
|
|
37
|
+
```bash
|
|
38
|
+
ap get_random_account "QQ"
|
|
39
|
+
ap get_random_account "YOUR_ACCOUNT_TYPE" --env_name specific_env
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Get account info by ID
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
ap get_account_by_id <account_id> [--env_name <environment_name>]
|
|
46
|
+
```
|
|
47
|
+
Example:
|
|
48
|
+
```bash
|
|
49
|
+
ap get_account_by_id "60c72b2f9b1d8f001f8e4d3a"
|
|
50
|
+
ap get_account_by_id "60c72b2f9b1d8f001f8e4d3b" --env_name specific_env
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Development
|
|
54
|
+
|
|
55
|
+
To set up for development:
|
|
56
|
+
|
|
57
|
+
1. Clone the repository.
|
|
58
|
+
2. Create a virtual environment: `python -m venv .venv`
|
|
59
|
+
3. Activate it: `source .venv/bin/activate` (on Linux/macOS) or `.venv\Scripts\activate` (on Windows)
|
|
60
|
+
4. Install dependencies: `pip install -r requirements-dev.txt` (You'll need to create this file if you have dev-specific dependencies like `pytest`)
|
|
61
|
+
5. Install the package in editable mode: `pip install -e .`
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
import httpx
|
|
4
|
+
import asyncio
|
|
5
|
+
import fire
|
|
6
|
+
import json
|
|
7
|
+
import random
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class AccountPoolCli:
|
|
11
|
+
"""
|
|
12
|
+
A CLI tool to interact with the Account Pool service.
|
|
13
|
+
Provides commands to fetch random accounts, specific account details by ID,
|
|
14
|
+
or specific account details by main number.
|
|
15
|
+
env_name defaults to 'webaqaxmn' if not provided for relevant commands.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
async def _fetch_random_account_async(self, env_name: str, account_type: str):
|
|
19
|
+
# Base part of the URL
|
|
20
|
+
base_url = "https://account-pool-mthor.int.rclabenv.com/accounts"
|
|
21
|
+
# Dictionary of query parameters; httpx will automatically handle URL encoding for these
|
|
22
|
+
params = {"envName": env_name, "accountType": account_type}
|
|
23
|
+
try:
|
|
24
|
+
async with httpx.AsyncClient() as client:
|
|
25
|
+
# Pass query conditions using the params argument
|
|
26
|
+
response = await client.get(base_url, params=params)
|
|
27
|
+
response.raise_for_status() # If the request fails (status code 4xx or 5xx), an HTTPStatusError exception will be raised
|
|
28
|
+
print(f"Request successful! Status code: {response.status_code}")
|
|
29
|
+
# Assume the response is in JSON format
|
|
30
|
+
try:
|
|
31
|
+
parsed_json = response.json() # Parse JSON first
|
|
32
|
+
accounts_list = parsed_json.get(
|
|
33
|
+
"accounts"
|
|
34
|
+
) # More safely get the 'accounts' list
|
|
35
|
+
|
|
36
|
+
if accounts_list: # Check if the list exists and is not empty
|
|
37
|
+
random_account = random.choice(
|
|
38
|
+
accounts_list
|
|
39
|
+
) # Randomly select an account
|
|
40
|
+
print("Randomly selected account information:")
|
|
41
|
+
# Print the entire randomly selected account dictionary (user previously changed to print the whole object)
|
|
42
|
+
print(json.dumps(random_account, indent=2, ensure_ascii=False))
|
|
43
|
+
else:
|
|
44
|
+
print(
|
|
45
|
+
"No matching accounts found, or the 'accounts' list is empty."
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
except (
|
|
49
|
+
json.JSONDecodeError,
|
|
50
|
+
TypeError,
|
|
51
|
+
KeyError,
|
|
52
|
+
) as e: # Removed IndexError because we now check the list first
|
|
53
|
+
print(f"Failed to parse JSON or extract account information: {e}")
|
|
54
|
+
print("Attempting to print raw response text:")
|
|
55
|
+
print(response.text)
|
|
56
|
+
if not isinstance(e, json.JSONDecodeError):
|
|
57
|
+
try:
|
|
58
|
+
print(
|
|
59
|
+
"Parsed JSON structure (may be incomplete or not as expected):"
|
|
60
|
+
)
|
|
61
|
+
print(
|
|
62
|
+
json.dumps(
|
|
63
|
+
response.json(), indent=2, ensure_ascii=False
|
|
64
|
+
)
|
|
65
|
+
)
|
|
66
|
+
except Exception as dump_e:
|
|
67
|
+
print(
|
|
68
|
+
f"Error occurred while trying to print parsed JSON: {dump_e}"
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
except httpx.HTTPStatusError as exc:
|
|
72
|
+
print(
|
|
73
|
+
f"Request failed, HTTP status code: {exc.response.status_code}, Error message: {exc.response.text}"
|
|
74
|
+
)
|
|
75
|
+
except httpx.RequestError as exc:
|
|
76
|
+
print(f"An error occurred during the request: {exc}")
|
|
77
|
+
|
|
78
|
+
def get_random_account(self, account_type: str, env_name: str = "webaqaxmn"):
|
|
79
|
+
"""
|
|
80
|
+
Fetches a random account from the Account Pool based on environment and account type.
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
account_type (str): Account type (e.g., \'\'\'kamino2(CI-Common-4U,mThor,brand=1210,packageId=26,packageVersion=1,pipeline=rc+glp)\'\'\').
|
|
84
|
+
env_name (str, optional): Environment name. Defaults to "webaqaxmn".
|
|
85
|
+
"""
|
|
86
|
+
asyncio.run(self._fetch_random_account_async(env_name, account_type))
|
|
87
|
+
|
|
88
|
+
async def _fetch_account_by_id_async(self, account_id: str, env_name: str):
|
|
89
|
+
# Construct URL with account_id in the path and env_name as a query parameter
|
|
90
|
+
url = f"https://account-pool-mthor.int.rclabenv.com/accounts/{account_id}"
|
|
91
|
+
params = {"envName": env_name}
|
|
92
|
+
|
|
93
|
+
print(
|
|
94
|
+
f"Fetching details for account ID {account_id} in env {env_name}..."
|
|
95
|
+
) # Added print for clarity
|
|
96
|
+
try:
|
|
97
|
+
async with httpx.AsyncClient() as client:
|
|
98
|
+
response = await client.get(url, params=params)
|
|
99
|
+
response.raise_for_status() # Raise an exception for bad status codes
|
|
100
|
+
print(
|
|
101
|
+
f"Request for ID {account_id} successful! Status code: {response.status_code}"
|
|
102
|
+
)
|
|
103
|
+
try:
|
|
104
|
+
account_details = response.json()
|
|
105
|
+
print("Account details:")
|
|
106
|
+
# Print the formatted JSON response
|
|
107
|
+
print(json.dumps(account_details, indent=2, ensure_ascii=False))
|
|
108
|
+
except json.JSONDecodeError as e:
|
|
109
|
+
print(f"Failed to parse JSON response for ID {account_id}: {e}")
|
|
110
|
+
print("Raw response text:")
|
|
111
|
+
print(response.text)
|
|
112
|
+
except httpx.HTTPStatusError as exc:
|
|
113
|
+
print(
|
|
114
|
+
f"Request for ID {account_id} failed, HTTP status code: {exc.response.status_code}, Error message: {exc.response.text}"
|
|
115
|
+
)
|
|
116
|
+
print("Raw response text (on error):")
|
|
117
|
+
print(exc.response.text)
|
|
118
|
+
except httpx.RequestError as exc:
|
|
119
|
+
print(f"An error occurred during the request for ID {account_id}: {exc}")
|
|
120
|
+
|
|
121
|
+
def get_account_by_id(self, account_id: str, env_name: str = "webaqaxmn"):
|
|
122
|
+
"""
|
|
123
|
+
Fetches specific account details by its ID and environment name.
|
|
124
|
+
|
|
125
|
+
Args:
|
|
126
|
+
account_id (str): The _id of the account to fetch.
|
|
127
|
+
env_name (str, optional): Environment name. Defaults to "webaqaxmn".
|
|
128
|
+
"""
|
|
129
|
+
asyncio.run(self._fetch_account_by_id_async(account_id, env_name))
|
|
130
|
+
|
|
131
|
+
async def _fetch_info_by_main_number_async(self, main_number: str, env_name: str):
|
|
132
|
+
# Ensure main_number is treated as a string for string operations
|
|
133
|
+
main_number_str = str(main_number)
|
|
134
|
+
# Automatically prepend '+' if not present
|
|
135
|
+
if not main_number_str.startswith("+"):
|
|
136
|
+
main_number_str = "+" + main_number_str
|
|
137
|
+
|
|
138
|
+
base_url = "https://account-pool-mthor.int.rclabenv.com/accounts"
|
|
139
|
+
params_initial_lookup = {"envName": env_name, "mainNumber": main_number_str}
|
|
140
|
+
print(
|
|
141
|
+
f"Looking up account ID for main number {main_number_str} in env {env_name}..."
|
|
142
|
+
)
|
|
143
|
+
try:
|
|
144
|
+
async with httpx.AsyncClient() as client:
|
|
145
|
+
response_initial = await client.get(
|
|
146
|
+
base_url, params=params_initial_lookup
|
|
147
|
+
)
|
|
148
|
+
response_initial.raise_for_status()
|
|
149
|
+
try:
|
|
150
|
+
parsed_json = response_initial.json()
|
|
151
|
+
accounts_list = parsed_json.get("accounts")
|
|
152
|
+
if accounts_list:
|
|
153
|
+
account_summary = accounts_list[0]
|
|
154
|
+
retrieved_account_id = account_summary.get("_id")
|
|
155
|
+
if retrieved_account_id:
|
|
156
|
+
print(
|
|
157
|
+
f"Found account ID: {retrieved_account_id}. Fetching details..."
|
|
158
|
+
)
|
|
159
|
+
await self._fetch_account_by_id_async(
|
|
160
|
+
retrieved_account_id, env_name
|
|
161
|
+
)
|
|
162
|
+
else:
|
|
163
|
+
print(
|
|
164
|
+
f"Account found for main number {main_number_str}, but it does not contain an '_id' field."
|
|
165
|
+
)
|
|
166
|
+
print("Account summary found:")
|
|
167
|
+
print(
|
|
168
|
+
json.dumps(
|
|
169
|
+
account_summary, indent=2, ensure_ascii=False
|
|
170
|
+
)
|
|
171
|
+
)
|
|
172
|
+
else:
|
|
173
|
+
print(
|
|
174
|
+
f"No account found for main number {main_number_str} in environment {env_name}."
|
|
175
|
+
)
|
|
176
|
+
except (json.JSONDecodeError, TypeError, KeyError, IndexError) as e:
|
|
177
|
+
print(
|
|
178
|
+
f"Failed to parse JSON or extract account ID from initial lookup: {e}"
|
|
179
|
+
)
|
|
180
|
+
print("Attempting to print raw response text from initial lookup:")
|
|
181
|
+
print(response_initial.text)
|
|
182
|
+
if not isinstance(e, json.JSONDecodeError):
|
|
183
|
+
try:
|
|
184
|
+
print(
|
|
185
|
+
"Parsed JSON structure from initial lookup (may be incomplete or not as expected):"
|
|
186
|
+
)
|
|
187
|
+
print(
|
|
188
|
+
json.dumps(
|
|
189
|
+
response_initial.json(),
|
|
190
|
+
indent=2,
|
|
191
|
+
ensure_ascii=False,
|
|
192
|
+
)
|
|
193
|
+
)
|
|
194
|
+
except Exception as dump_e:
|
|
195
|
+
print(
|
|
196
|
+
f"Error occurred while trying to print parsed JSON from initial lookup: {dump_e}"
|
|
197
|
+
)
|
|
198
|
+
except httpx.HTTPStatusError as exc:
|
|
199
|
+
print(
|
|
200
|
+
f"Initial lookup for main number {main_number_str} failed, HTTP status code: {exc.response.status_code}, Error message: {exc.response.text}"
|
|
201
|
+
)
|
|
202
|
+
print("Raw response text (on error):")
|
|
203
|
+
print(exc.response.text)
|
|
204
|
+
except httpx.RequestError as exc:
|
|
205
|
+
print(
|
|
206
|
+
f"An error occurred during the initial lookup for main number {main_number_str}: {exc}"
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
def info(self, main_number: str, env_name: str = "webaqaxmn"):
|
|
210
|
+
"""
|
|
211
|
+
Fetches specific account details by its mainNumber.
|
|
212
|
+
This first looks up the account by mainNumber to get its _id, then fetches full details using the _id.
|
|
213
|
+
If main_number is provided without a leading '+', it will be added automatically.
|
|
214
|
+
|
|
215
|
+
Args:
|
|
216
|
+
main_number (str): The mainNumber of the account to fetch (e.g., 12495002020 or +12495002020).
|
|
217
|
+
env_name (str, optional): Environment name. Defaults to "webaqaxmn".
|
|
218
|
+
"""
|
|
219
|
+
asyncio.run(self._fetch_info_by_main_number_async(main_number, env_name))
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def main_cli_function():
|
|
223
|
+
fire.Fire(AccountPoolCli)
|
|
224
|
+
|
|
225
|
+
if __name__ == "__main__":
|
|
226
|
+
main_cli_function()
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
import json
|
|
5
|
+
import fire
|
|
6
|
+
import httpx
|
|
7
|
+
|
|
8
|
+
BASE_URL = "https://device-spy-mthor.int.rclabenv.com"
|
|
9
|
+
HOSTS_ENDPOINT = BASE_URL + "/api/v1/hosts"
|
|
10
|
+
ALL_DEVICES_ENDPOINT = BASE_URL + "/api/v1/hosts/get_all_devices"
|
|
11
|
+
LABELS_ENDPOINT = BASE_URL + "/api/v1/labels/"
|
|
12
|
+
DEVICE_ASSETS_ENDPOINT = BASE_URL + "/api/v1/device_assets/"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class DeviceSpyCli:
|
|
16
|
+
"""A CLI tool to interact with the Device Spy service.
|
|
17
|
+
|
|
18
|
+
This tool allows you to query various details about devices and hosts
|
|
19
|
+
managed by the Device Spy system.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
def _get_device_location_from_assets(self, udid):
|
|
23
|
+
try:
|
|
24
|
+
with httpx.Client() as client:
|
|
25
|
+
response = client.get(DEVICE_ASSETS_ENDPOINT)
|
|
26
|
+
response.raise_for_status()
|
|
27
|
+
device_assets = response.json().get("data", [])
|
|
28
|
+
for device_asset in device_assets:
|
|
29
|
+
if device_asset.get("udid") == udid:
|
|
30
|
+
return device_asset.get("location")
|
|
31
|
+
except httpx.RequestError as e:
|
|
32
|
+
print(f"Error fetching device assets: {e}")
|
|
33
|
+
except json.JSONDecodeError:
|
|
34
|
+
print("Error decoding JSON from device assets response.")
|
|
35
|
+
return None
|
|
36
|
+
|
|
37
|
+
def _get_host_alias(self, host_ip):
|
|
38
|
+
try:
|
|
39
|
+
with httpx.Client() as client:
|
|
40
|
+
response = client.get(HOSTS_ENDPOINT)
|
|
41
|
+
response.raise_for_status()
|
|
42
|
+
hosts = response.json().get("data", [])
|
|
43
|
+
for host in hosts:
|
|
44
|
+
if host.get("hostname") == host_ip:
|
|
45
|
+
return host.get("alias")
|
|
46
|
+
except httpx.RequestError as e:
|
|
47
|
+
print(f"Error fetching hosts: {e}")
|
|
48
|
+
except json.JSONDecodeError:
|
|
49
|
+
print("Error decoding JSON from hosts response.")
|
|
50
|
+
return None
|
|
51
|
+
|
|
52
|
+
def info(self, udid: str):
|
|
53
|
+
"""Queries and displays detailed information for a specific device.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
udid (str): The Unique Device Identifier (UDID) of the device to query.
|
|
57
|
+
"""
|
|
58
|
+
try:
|
|
59
|
+
with httpx.Client() as client:
|
|
60
|
+
response = client.get(ALL_DEVICES_ENDPOINT)
|
|
61
|
+
response.raise_for_status()
|
|
62
|
+
devices = response.json().get("data", [])
|
|
63
|
+
for device_data in devices:
|
|
64
|
+
if udid == device_data.get("udid"):
|
|
65
|
+
device_info = device_data.copy()
|
|
66
|
+
original_hostname = device_info.get("hostname")
|
|
67
|
+
|
|
68
|
+
device_info["hostname"] = self._get_host_alias(
|
|
69
|
+
original_hostname
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
if device_info.get("platform") == "android":
|
|
73
|
+
device_info["ip_port"] = (
|
|
74
|
+
f"{original_hostname}:{device_info.get('adb_port')}"
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
location = self._get_device_location_from_assets(udid)
|
|
78
|
+
if location:
|
|
79
|
+
device_info["location"] = location
|
|
80
|
+
|
|
81
|
+
keys_to_delete = ["is_simulator", "remote_control", "adb_port"]
|
|
82
|
+
|
|
83
|
+
for key in keys_to_delete:
|
|
84
|
+
if key in device_info:
|
|
85
|
+
del device_info[key]
|
|
86
|
+
|
|
87
|
+
print(json.dumps(device_info, indent=2, ensure_ascii=False))
|
|
88
|
+
return
|
|
89
|
+
print(f"Device with UDID '{udid}' not found.")
|
|
90
|
+
except httpx.RequestError as e:
|
|
91
|
+
print(f"Error fetching all devices: {e}")
|
|
92
|
+
except json.JSONDecodeError:
|
|
93
|
+
print("Error decoding JSON from all devices response.")
|
|
94
|
+
|
|
95
|
+
def available_devices(self, platform: str):
|
|
96
|
+
"""Lists available (not locked, not simulator) devices for a given platform.
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
platform (str): The platform to filter by (e.g., "android", "ios").
|
|
100
|
+
"""
|
|
101
|
+
try:
|
|
102
|
+
with httpx.Client() as client:
|
|
103
|
+
response = client.get(ALL_DEVICES_ENDPOINT)
|
|
104
|
+
response.raise_for_status()
|
|
105
|
+
all_devices = response.json().get("data", [])
|
|
106
|
+
avail_devices_udids = []
|
|
107
|
+
|
|
108
|
+
for device in all_devices:
|
|
109
|
+
if (
|
|
110
|
+
not device.get("is_locked")
|
|
111
|
+
and not device.get("is_simulator")
|
|
112
|
+
and device.get("platform") == platform
|
|
113
|
+
):
|
|
114
|
+
avail_devices_udids.append(device.get("udid"))
|
|
115
|
+
|
|
116
|
+
result = {
|
|
117
|
+
"count": len(avail_devices_udids),
|
|
118
|
+
"udids": avail_devices_udids,
|
|
119
|
+
}
|
|
120
|
+
print(json.dumps(result, indent=2, ensure_ascii=False))
|
|
121
|
+
|
|
122
|
+
except httpx.RequestError as e:
|
|
123
|
+
print(f"Error fetching available devices: {e}")
|
|
124
|
+
except json.JSONDecodeError:
|
|
125
|
+
print("Error decoding JSON from available devices response.")
|
|
126
|
+
|
|
127
|
+
def get_host_ip(self, query_string: str):
|
|
128
|
+
"""Finds host IP address(es) based on a query string.
|
|
129
|
+
|
|
130
|
+
The query string is matched against host information fields like alias or hostname.
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
query_string (str): The string to search for within host information.
|
|
134
|
+
"""
|
|
135
|
+
try:
|
|
136
|
+
with httpx.Client() as client:
|
|
137
|
+
response = client.get(HOSTS_ENDPOINT)
|
|
138
|
+
response.raise_for_status()
|
|
139
|
+
hosts = response.json().get("data", [])
|
|
140
|
+
found_host_ips = []
|
|
141
|
+
for host in hosts:
|
|
142
|
+
for value in host.values():
|
|
143
|
+
if query_string.lower() in str(value).lower():
|
|
144
|
+
found_host_ips.append(host.get("hostname"))
|
|
145
|
+
break
|
|
146
|
+
|
|
147
|
+
if not found_host_ips:
|
|
148
|
+
print(f"No host found matching '{query_string}'.")
|
|
149
|
+
elif len(found_host_ips) == 1:
|
|
150
|
+
print(found_host_ips[0])
|
|
151
|
+
else:
|
|
152
|
+
print(json.dumps(found_host_ips, indent=2))
|
|
153
|
+
|
|
154
|
+
except httpx.RequestError as e:
|
|
155
|
+
print(f"Error fetching hosts for IP lookup: {e}")
|
|
156
|
+
except json.JSONDecodeError:
|
|
157
|
+
print("Error decoding JSON from hosts response for IP lookup.")
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def main_ds_function():
|
|
161
|
+
fire.Fire(DeviceSpyCli)
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
# if __name__ == '__main__':
|
|
165
|
+
# main_ds_function() # Keep this commented out for library use
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: my-cli-utilities
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A suite of CLI utilities for account pool and device spy services.
|
|
5
|
+
Author-email: Swain Zheng <swain.zheng@xxxx.com>
|
|
6
|
+
License: MIT License
|
|
7
|
+
Project-URL: Homepage, https://github.com/yourusername/my-cli-utilities
|
|
8
|
+
Project-URL: Bug Tracker, https://github.com/yourusername/my-cli-utilities/issues
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Topic :: Utilities
|
|
14
|
+
Requires-Python: >=3.8
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
License-File: LICENSE
|
|
17
|
+
Requires-Dist: httpx>=0.20
|
|
18
|
+
Requires-Dist: fire>=0.4
|
|
19
|
+
Requires-Dist: requests>=2.20
|
|
20
|
+
Dynamic: license-file
|
|
21
|
+
|
|
22
|
+
# Account Pool CLI
|
|
23
|
+
|
|
24
|
+
A simple CLI tool to interact with the account pool service, allowing you to fetch account details.
|
|
25
|
+
|
|
26
|
+
## Installation
|
|
27
|
+
|
|
28
|
+
To install the Account Pool CLI, run the following command in your terminal:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pip install .
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
(If you publish it to PyPI, you would use `pip install account-pool-cli`)
|
|
35
|
+
|
|
36
|
+
## Usage
|
|
37
|
+
|
|
38
|
+
Once installed, you can use the `ap` command:
|
|
39
|
+
|
|
40
|
+
### Get account info by main number
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
ap info <main_number> [--env_name <environment_name>]
|
|
44
|
+
```
|
|
45
|
+
Example:
|
|
46
|
+
```bash
|
|
47
|
+
ap info 1234567890
|
|
48
|
+
ap info +1234567890 --env_name specific_env
|
|
49
|
+
```
|
|
50
|
+
`env_name` defaults to `webaqaxmn` if not provided.
|
|
51
|
+
|
|
52
|
+
### Get a random account
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
ap get_random_account <account_type> [--env_name <environment_name>]
|
|
56
|
+
```
|
|
57
|
+
Example:
|
|
58
|
+
```bash
|
|
59
|
+
ap get_random_account "QQ"
|
|
60
|
+
ap get_random_account "YOUR_ACCOUNT_TYPE" --env_name specific_env
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Get account info by ID
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
ap get_account_by_id <account_id> [--env_name <environment_name>]
|
|
67
|
+
```
|
|
68
|
+
Example:
|
|
69
|
+
```bash
|
|
70
|
+
ap get_account_by_id "60c72b2f9b1d8f001f8e4d3a"
|
|
71
|
+
ap get_account_by_id "60c72b2f9b1d8f001f8e4d3b" --env_name specific_env
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Development
|
|
75
|
+
|
|
76
|
+
To set up for development:
|
|
77
|
+
|
|
78
|
+
1. Clone the repository.
|
|
79
|
+
2. Create a virtual environment: `python -m venv .venv`
|
|
80
|
+
3. Activate it: `source .venv/bin/activate` (on Linux/macOS) or `.venv\Scripts\activate` (on Windows)
|
|
81
|
+
4. Install dependencies: `pip install -r requirements-dev.txt` (You'll need to create this file if you have dev-specific dependencies like `pytest`)
|
|
82
|
+
5. Install the package in editable mode: `pip install -e .`
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
account_pool_cli/__init__.py
|
|
5
|
+
account_pool_cli/cli.py
|
|
6
|
+
device_spy_cli/__init__.py
|
|
7
|
+
device_spy_cli/cli.py
|
|
8
|
+
my_cli_utilities.egg-info/PKG-INFO
|
|
9
|
+
my_cli_utilities.egg-info/SOURCES.txt
|
|
10
|
+
my_cli_utilities.egg-info/dependency_links.txt
|
|
11
|
+
my_cli_utilities.egg-info/entry_points.txt
|
|
12
|
+
my_cli_utilities.egg-info/requires.txt
|
|
13
|
+
my_cli_utilities.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "my-cli-utilities"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
authors = [
|
|
9
|
+
{ name="Swain Zheng", email="swain.zheng@xxxx.com" },
|
|
10
|
+
]
|
|
11
|
+
description = "A suite of CLI utilities for account pool and device spy services."
|
|
12
|
+
readme = "README.md"
|
|
13
|
+
requires-python = ">=3.8"
|
|
14
|
+
license = {text = "MIT License"}
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Programming Language :: Python :: 3",
|
|
17
|
+
"Operating System :: OS Independent",
|
|
18
|
+
"Development Status :: 3 - Alpha",
|
|
19
|
+
"Intended Audience :: Developers",
|
|
20
|
+
"Topic :: Utilities",
|
|
21
|
+
]
|
|
22
|
+
dependencies = [
|
|
23
|
+
"httpx>=0.20",
|
|
24
|
+
"fire>=0.4",
|
|
25
|
+
"requests>=2.20"
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
[project.urls]
|
|
29
|
+
"Homepage" = "https://github.com/yourusername/my-cli-utilities"
|
|
30
|
+
"Bug Tracker" = "https://github.com/yourusername/my-cli-utilities/issues"
|
|
31
|
+
|
|
32
|
+
[project.scripts]
|
|
33
|
+
ap = "account_pool_cli.cli:main_cli_function"
|
|
34
|
+
ds = "device_spy_cli.cli:main_ds_function"
|
|
35
|
+
|
|
36
|
+
[tool.setuptools.packages.find]
|
|
37
|
+
include = ["account_pool_cli*", "device_spy_cli*"]
|
|
38
|
+
exclude = ["plists*" ]
|