amazon-ads-cli 0.1.2__tar.gz → 0.1.3__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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Stellar Aether
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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: amazon-ads-cli
3
- Version: 0.1.2
3
+ Version: 0.1.3
4
4
  Summary: CLI tool for Amazon Advertising API v3
5
5
  Home-page: https://github.com/stellaraether/amazon-ads-cli
6
6
  Author: Lunan Li
@@ -14,6 +14,7 @@ Classifier: Programming Language :: Python :: 3.9
14
14
  Classifier: Programming Language :: Python :: 3.10
15
15
  Classifier: Programming Language :: Python :: 3.11
16
16
  Requires-Python: >=3.8
17
+ License-File: LICENSE
17
18
  Requires-Dist: click>=8.0
18
19
  Requires-Dist: python-amazon-ad-api>=0.8.0
19
20
  Requires-Dist: requests>=2.27.0
@@ -21,6 +22,7 @@ Dynamic: author
21
22
  Dynamic: author-email
22
23
  Dynamic: classifier
23
24
  Dynamic: home-page
25
+ Dynamic: license-file
24
26
  Dynamic: requires-dist
25
27
  Dynamic: requires-python
26
28
  Dynamic: summary
@@ -0,0 +1,6 @@
1
+ """Entry point for python -m amazon_ads_cli."""
2
+
3
+ from amazon_ads_cli.main import cli
4
+
5
+ if __name__ == "__main__":
6
+ cli()
@@ -2,22 +2,142 @@
2
2
  """Amazon Ads CLI - Command line interface for Amazon Advertising API v3."""
3
3
 
4
4
  import json
5
+ import os
5
6
  from datetime import datetime, timedelta
6
7
 
7
8
  import click
9
+ import yaml
8
10
  from ad_api.api import reports, sponsored_products
9
11
  from ad_api.base import Marketplaces
10
12
 
13
+ DEFAULT_CREDENTIALS_PATH = os.path.expanduser("~/.config/python-ad-api/credentials.yml")
14
+
15
+
16
+ def _check_path():
17
+ """Check if the CLI is accessible in PATH and warn if not."""
18
+ import shutil
19
+ import sys
20
+
21
+ if not shutil.which("amz-ads"):
22
+ print(
23
+ "\n⚠️ Note: 'amz-ads' is not in your PATH.",
24
+ file=sys.stderr,
25
+ )
26
+ print(
27
+ " You can still use: python3 -m amazon_ads_cli",
28
+ file=sys.stderr,
29
+ )
30
+ print(
31
+ " To add to PATH, add this to your shell config:",
32
+ file=sys.stderr,
33
+ )
34
+ print(
35
+ f' export PATH="{sys.prefix}/bin:$PATH"',
36
+ file=sys.stderr,
37
+ )
38
+ print("", file=sys.stderr)
39
+
11
40
 
12
41
  @click.group()
13
42
  @click.option("--profile", "-p", default="default", help="Credential profile")
14
43
  @click.pass_context
15
44
  def cli(ctx, profile):
16
45
  """Amazon Ads CLI - Manage campaigns, keywords, and reports."""
46
+ _check_path()
17
47
  ctx.ensure_object(dict)
18
48
  ctx.obj["profile"] = profile
19
49
 
20
50
 
51
+ @cli.group()
52
+ def auth():
53
+ """Authentication commands."""
54
+ pass
55
+
56
+
57
+ @auth.command("setup")
58
+ @click.option(
59
+ "--path", default=DEFAULT_CREDENTIALS_PATH, help="Path to save credentials"
60
+ )
61
+ @click.pass_context
62
+ def auth_setup(ctx, path):
63
+ """Interactive setup for Amazon Ads API credentials."""
64
+ click.echo("🔐 Amazon Ads API Credential Setup")
65
+ click.echo("=" * 50)
66
+ click.echo()
67
+ click.echo("You'll need the following from your Amazon Developer account:")
68
+ click.echo(" 1. Refresh Token (from LWA authorization)")
69
+ click.echo(" 2. Client ID (from your app registration)")
70
+ click.echo(" 3. Client Secret (from your app registration)")
71
+ click.echo(" 4. Profile ID (your Amazon Ads account ID)")
72
+ click.echo()
73
+
74
+ profile = click.prompt("Profile name", default="default")
75
+ refresh_token = click.prompt("Refresh token", hide_input=True)
76
+ client_id = click.prompt("Client ID")
77
+ client_secret = click.prompt("Client secret", hide_input=True)
78
+ profile_id = click.prompt("Profile ID (numeric)")
79
+
80
+ credentials = {
81
+ "version": "1.0",
82
+ profile: {
83
+ "refresh_token": refresh_token,
84
+ "client_id": client_id,
85
+ "client_secret": client_secret,
86
+ "profile_id": profile_id,
87
+ },
88
+ }
89
+
90
+ # Merge with existing if present
91
+ if os.path.exists(path):
92
+ try:
93
+ with open(path, "r") as f:
94
+ existing = yaml.safe_load(f) or {}
95
+ existing[profile] = credentials[profile]
96
+ credentials = existing
97
+ click.echo(f"\n📝 Merged with existing credentials at {path}")
98
+ except Exception as e:
99
+ click.echo(f"⚠️ Could not read existing file: {e}")
100
+
101
+ os.makedirs(os.path.dirname(path), exist_ok=True)
102
+ with open(path, "w") as f:
103
+ yaml.dump(credentials, f, default_flow_style=False, sort_keys=False)
104
+
105
+ click.echo(f"✅ Credentials saved to {path}")
106
+ click.echo(f" Profile: {profile}")
107
+ click.echo(f" Profile ID: {profile_id}")
108
+ click.echo()
109
+ click.echo(
110
+ "You can now use: python -m amazon_ads_cli.main --profile {profile} campaigns list"
111
+ )
112
+
113
+
114
+ @auth.command("show")
115
+ @click.option(
116
+ "--path", default=DEFAULT_CREDENTIALS_PATH, help="Path to credentials file"
117
+ )
118
+ @click.pass_context
119
+ def auth_show(ctx, path):
120
+ """Show configured profiles (without secrets)."""
121
+ if not os.path.exists(path):
122
+ click.echo(f"❌ No credentials file found at {path}")
123
+ click.echo("Run: python -m amazon_ads_cli.main auth setup")
124
+ return
125
+
126
+ with open(path, "r") as f:
127
+ creds = yaml.safe_load(f) or {}
128
+
129
+ click.echo(f"\n📄 Credentials file: {path}")
130
+ click.echo("-" * 40)
131
+
132
+ for profile, data in creds.items():
133
+ if profile == "version":
134
+ continue
135
+ click.echo(f"Profile: {profile}")
136
+ click.echo(f" Client ID: {data.get('client_id', 'N/A')[:20]}...")
137
+ click.echo(f" Profile ID: {data.get('profile_id', 'N/A')}")
138
+ click.echo()
139
+
140
+
21
141
  @cli.group()
22
142
  def campaigns():
23
143
  """Campaign management commands."""
@@ -181,7 +301,12 @@ def list_negatives(ctx, campaign_id):
181
301
  """List negative keywords for a campaign."""
182
302
  result = sponsored_products.NegativeKeywordsV3(
183
303
  marketplace=Marketplaces.NA
184
- ).list_negative_keywords(body={"campaignIdFilter": {"include": [campaign_id]}})
304
+ ).list_negative_keywords(
305
+ body={
306
+ "campaignIdFilter": {"include": [campaign_id]},
307
+ "stateFilter": {"include": ["ENABLED"]},
308
+ }
309
+ )
185
310
  negatives = result.payload.get("negativeKeywords", [])
186
311
 
187
312
  click.echo(f"\n{'Negative Keyword':<35} {'Match':<15}")
@@ -194,6 +319,7 @@ def list_negatives(ctx, campaign_id):
194
319
 
195
320
  @negatives.command("add")
196
321
  @click.argument("campaign-id")
322
+ @click.argument("ad-group-id")
197
323
  @click.argument("keyword-text")
198
324
  @click.option(
199
325
  "--match-type",
@@ -201,16 +327,17 @@ def list_negatives(ctx, campaign_id):
201
327
  help="Match type: NEGATIVE_EXACT, NEGATIVE_PHRASE",
202
328
  )
203
329
  @click.pass_context
204
- def add_negative(ctx, campaign_id, keyword_text, match_type):
330
+ def add_negative(ctx, campaign_id, ad_group_id, keyword_text, match_type):
205
331
  """Add a negative keyword to a campaign."""
206
332
  try:
207
333
  sponsored_products.NegativeKeywordsV3(
208
334
  marketplace=Marketplaces.NA
209
- ).create_negative_keywords(
335
+ ).create_negative_keyword(
210
336
  body={
211
337
  "negativeKeywords": [
212
338
  {
213
339
  "campaignId": campaign_id,
340
+ "adGroupId": ad_group_id,
214
341
  "keywordText": keyword_text,
215
342
  "matchType": match_type,
216
343
  "state": "ENABLED",
@@ -223,6 +350,22 @@ def add_negative(ctx, campaign_id, keyword_text, match_type):
223
350
  click.echo(f"❌ Error: {e}")
224
351
 
225
352
 
353
+ @negatives.command("remove")
354
+ @click.argument("negative-keyword-id")
355
+ @click.pass_context
356
+ def remove_negative(ctx, negative_keyword_id):
357
+ """Remove a negative keyword by ID."""
358
+ try:
359
+ sponsored_products.NegativeKeywordsV3(
360
+ marketplace=Marketplaces.NA
361
+ ).delete_negative_keywords(
362
+ body={"negativeKeywordIdFilter": {"include": [negative_keyword_id]}}
363
+ )
364
+ click.echo(f"✅ Removed negative keyword: {negative_keyword_id}")
365
+ except Exception as e:
366
+ click.echo(f"❌ Error: {e}")
367
+
368
+
226
369
  @cli.group()
227
370
  def report():
228
371
  """Report commands."""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: amazon-ads-cli
3
- Version: 0.1.2
3
+ Version: 0.1.3
4
4
  Summary: CLI tool for Amazon Advertising API v3
5
5
  Home-page: https://github.com/stellaraether/amazon-ads-cli
6
6
  Author: Lunan Li
@@ -14,6 +14,7 @@ Classifier: Programming Language :: Python :: 3.9
14
14
  Classifier: Programming Language :: Python :: 3.10
15
15
  Classifier: Programming Language :: Python :: 3.11
16
16
  Requires-Python: >=3.8
17
+ License-File: LICENSE
17
18
  Requires-Dist: click>=8.0
18
19
  Requires-Dist: python-amazon-ad-api>=0.8.0
19
20
  Requires-Dist: requests>=2.27.0
@@ -21,6 +22,7 @@ Dynamic: author
21
22
  Dynamic: author-email
22
23
  Dynamic: classifier
23
24
  Dynamic: home-page
25
+ Dynamic: license-file
24
26
  Dynamic: requires-dist
25
27
  Dynamic: requires-python
26
28
  Dynamic: summary
@@ -1,6 +1,8 @@
1
+ LICENSE
1
2
  README.md
2
3
  setup.py
3
4
  amazon_ads_cli/__init__.py
5
+ amazon_ads_cli/__main__.py
4
6
  amazon_ads_cli/main.py
5
7
  amazon_ads_cli.egg-info/PKG-INFO
6
8
  amazon_ads_cli.egg-info/SOURCES.txt
@@ -2,7 +2,7 @@ from setuptools import find_packages, setup
2
2
 
3
3
  setup(
4
4
  name="amazon-ads-cli",
5
- version="0.1.2",
5
+ version="0.1.3",
6
6
  description="CLI tool for Amazon Advertising API v3",
7
7
  author="Lunan Li",
8
8
  author_email="lunan@stellaraether.com",
File without changes
File without changes