direct-cli 0.0.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.
Files changed (39) hide show
  1. direct_cli/__init__.py +14 -0
  2. direct_cli/api.py +94 -0
  3. direct_cli/auth.py +58 -0
  4. direct_cli/cli.py +85 -0
  5. direct_cli/commands/__init__.py +61 -0
  6. direct_cli/commands/adextensions.py +96 -0
  7. direct_cli/commands/adgroups.py +189 -0
  8. direct_cli/commands/adimages.py +63 -0
  9. direct_cli/commands/ads.py +306 -0
  10. direct_cli/commands/agencyclients.py +64 -0
  11. direct_cli/commands/audiencetargets.py +187 -0
  12. direct_cli/commands/bidmodifiers.py +110 -0
  13. direct_cli/commands/bids.py +108 -0
  14. direct_cli/commands/businesses.py +61 -0
  15. direct_cli/commands/campaigns.py +311 -0
  16. direct_cli/commands/changes.py +97 -0
  17. direct_cli/commands/clients.py +98 -0
  18. direct_cli/commands/creatives.py +68 -0
  19. direct_cli/commands/dictionaries.py +64 -0
  20. direct_cli/commands/dynamicads.py +104 -0
  21. direct_cli/commands/feeds.py +99 -0
  22. direct_cli/commands/keywordbids.py +111 -0
  23. direct_cli/commands/keywords.py +309 -0
  24. direct_cli/commands/keywordsresearch.py +71 -0
  25. direct_cli/commands/leads.py +65 -0
  26. direct_cli/commands/negativekeywordsharedsets.py +97 -0
  27. direct_cli/commands/reports.py +128 -0
  28. direct_cli/commands/retargeting.py +104 -0
  29. direct_cli/commands/sitelinks.py +92 -0
  30. direct_cli/commands/smartadtargets.py +104 -0
  31. direct_cli/commands/turbopages.py +97 -0
  32. direct_cli/commands/vcards.py +93 -0
  33. direct_cli/output.py +143 -0
  34. direct_cli/utils.py +120 -0
  35. direct_cli-0.0.0.dist-info/METADATA +393 -0
  36. direct_cli-0.0.0.dist-info/RECORD +39 -0
  37. direct_cli-0.0.0.dist-info/WHEEL +5 -0
  38. direct_cli-0.0.0.dist-info/entry_points.txt +2 -0
  39. direct_cli-0.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,104 @@
1
+ """
2
+ RetargetingLists commands
3
+ """
4
+
5
+ import json
6
+ import click
7
+
8
+ from ..api import create_client
9
+ from ..output import format_output, print_error
10
+ from ..utils import parse_ids, get_default_fields
11
+
12
+
13
+ @click.group()
14
+ def retargeting():
15
+ """Manage retargeting lists"""
16
+ pass
17
+
18
+
19
+ @retargeting.command()
20
+ @click.option("--ids", help="Comma-separated list IDs")
21
+ @click.option("--types", help="Filter by types")
22
+ @click.option("--limit", type=int, help="Limit number of results")
23
+ @click.option("--fetch-all", is_flag=True, help="Fetch all pages")
24
+ @click.option("--format", "output_format", default="json", help="Output format")
25
+ @click.option("--output", help="Output file")
26
+ @click.option("--fields", help="Comma-separated field names")
27
+ @click.pass_context
28
+ def get(ctx, ids, types, limit, fetch_all, output_format, output, fields):
29
+ """Get retargeting lists"""
30
+ try:
31
+ client = create_client(
32
+ token=ctx.obj.get("token"),
33
+ login=ctx.obj.get("login"),
34
+ sandbox=ctx.obj.get("sandbox"),
35
+ )
36
+
37
+ field_names = (
38
+ fields.split(",") if fields else get_default_fields("retargetinglists")
39
+ )
40
+
41
+ criteria = {}
42
+ if ids:
43
+ criteria["Ids"] = parse_ids(ids)
44
+ if types:
45
+ criteria["Types"] = types.split(",")
46
+
47
+ params = {"SelectionCriteria": criteria, "FieldNames": field_names}
48
+
49
+ if limit:
50
+ params["Page"] = {"Limit": limit}
51
+
52
+ body = {"method": "get", "params": params}
53
+
54
+ result = client.retargeting().post(data=body)
55
+
56
+ if fetch_all:
57
+ items = []
58
+ for item in result().iter_items():
59
+ items.append(item)
60
+ format_output(items, output_format, output)
61
+ else:
62
+ data = result().extract()
63
+ format_output(data, output_format, output)
64
+
65
+ except Exception as e:
66
+ print_error(str(e))
67
+ raise click.Abort()
68
+
69
+
70
+ @retargeting.command()
71
+ @click.option("--name", required=True, help="List name")
72
+ @click.option(
73
+ "--type", "list_type", required=True, help="List type (AUDIENCE_SEGMENT, etc.)"
74
+ )
75
+ @click.option("--json", "extra_json", help="Additional JSON parameters")
76
+ @click.option("--dry-run", is_flag=True, help="Show request without sending")
77
+ @click.pass_context
78
+ def add(ctx, name, list_type, extra_json, dry_run):
79
+ """Add new retargeting list"""
80
+ try:
81
+ list_data = {"Name": name, "Type": list_type}
82
+
83
+ if extra_json:
84
+ extra = json.loads(extra_json)
85
+ list_data.update(extra)
86
+
87
+ body = {"method": "add", "params": {"RetargetingLists": [list_data]}}
88
+
89
+ if dry_run:
90
+ format_output(body, "json", None)
91
+ return
92
+
93
+ client = create_client(
94
+ token=ctx.obj.get("token"),
95
+ login=ctx.obj.get("login"),
96
+ sandbox=ctx.obj.get("sandbox"),
97
+ )
98
+
99
+ result = client.retargeting().post(data=body)
100
+ format_output(result().extract(), "json", None)
101
+
102
+ except Exception as e:
103
+ print_error(str(e))
104
+ raise click.Abort()
@@ -0,0 +1,92 @@
1
+ """
2
+ Sitelinks commands
3
+ """
4
+
5
+ import json
6
+ import click
7
+
8
+ from ..api import create_client
9
+ from ..output import format_output, print_error
10
+ from ..utils import parse_ids
11
+
12
+
13
+ @click.group()
14
+ def sitelinks():
15
+ """Manage sitelinks"""
16
+ pass
17
+
18
+
19
+ @sitelinks.command()
20
+ @click.option("--ids", help="Comma-separated sitelink IDs")
21
+ @click.option("--limit", type=int, help="Limit number of results")
22
+ @click.option("--fetch-all", is_flag=True, help="Fetch all pages")
23
+ @click.option("--format", "output_format", default="json", help="Output format")
24
+ @click.option("--output", help="Output file")
25
+ @click.option("--fields", help="Comma-separated field names")
26
+ @click.pass_context
27
+ def get(ctx, ids, limit, fetch_all, output_format, output, fields):
28
+ """Get sitelinks"""
29
+ try:
30
+ client = create_client(
31
+ token=ctx.obj.get("token"),
32
+ login=ctx.obj.get("login"),
33
+ sandbox=ctx.obj.get("sandbox"),
34
+ )
35
+
36
+ field_names = fields.split(",") if fields else ["Id", "Sitelinks"]
37
+
38
+ criteria = {}
39
+ if ids:
40
+ criteria["Ids"] = parse_ids(ids)
41
+
42
+ params = {"SelectionCriteria": criteria, "FieldNames": field_names}
43
+
44
+ if limit:
45
+ params["Page"] = {"Limit": limit}
46
+
47
+ body = {"method": "get", "params": params}
48
+
49
+ result = client.sitelinks().post(data=body)
50
+
51
+ if fetch_all:
52
+ items = []
53
+ for item in result().iter_items():
54
+ items.append(item)
55
+ format_output(items, output_format, output)
56
+ else:
57
+ data = result().extract()
58
+ format_output(data, output_format, output)
59
+
60
+ except Exception as e:
61
+ print_error(str(e))
62
+ raise click.Abort()
63
+
64
+
65
+ @sitelinks.command()
66
+ @click.option("--links", required=True, help="JSON array of sitelinks")
67
+ @click.option("--dry-run", is_flag=True, help="Show request without sending")
68
+ @click.pass_context
69
+ def add(ctx, links, dry_run):
70
+ """Add sitelinks set"""
71
+ try:
72
+ body = {
73
+ "method": "add",
74
+ "params": {"SitelinksSets": [{"Sitelinks": json.loads(links)}]},
75
+ }
76
+
77
+ if dry_run:
78
+ format_output(body, "json", None)
79
+ return
80
+
81
+ client = create_client(
82
+ token=ctx.obj.get("token"),
83
+ login=ctx.obj.get("login"),
84
+ sandbox=ctx.obj.get("sandbox"),
85
+ )
86
+
87
+ result = client.sitelinks().post(data=body)
88
+ format_output(result().extract(), "json", None)
89
+
90
+ except Exception as e:
91
+ print_error(str(e))
92
+ raise click.Abort()
@@ -0,0 +1,104 @@
1
+ """
2
+ SmartAdTargets commands
3
+ """
4
+
5
+ import json
6
+ import click
7
+
8
+ from ..api import create_client
9
+ from ..output import format_output, print_error
10
+ from ..utils import parse_ids
11
+
12
+
13
+ @click.group()
14
+ def smartadtargets():
15
+ """Manage smart ad targets"""
16
+ pass
17
+
18
+
19
+ @smartadtargets.command()
20
+ @click.option("--ids", help="Comma-separated target IDs")
21
+ @click.option("--adgroup-ids", help="Comma-separated ad group IDs")
22
+ @click.option("--limit", type=int, help="Limit number of results")
23
+ @click.option("--fetch-all", is_flag=True, help="Fetch all pages")
24
+ @click.option("--format", "output_format", default="json", help="Output format")
25
+ @click.option("--output", help="Output file")
26
+ @click.option("--fields", help="Comma-separated field names")
27
+ @click.pass_context
28
+ def get(ctx, ids, adgroup_ids, limit, fetch_all, output_format, output, fields):
29
+ """Get smart ad targets"""
30
+ try:
31
+ client = create_client(
32
+ token=ctx.obj.get("token"),
33
+ login=ctx.obj.get("login"),
34
+ sandbox=ctx.obj.get("sandbox"),
35
+ )
36
+
37
+ field_names = (
38
+ fields.split(",")
39
+ if fields
40
+ else ["Id", "CampaignId", "AdGroupId", "Status", "ServingStatus"]
41
+ )
42
+
43
+ criteria = {}
44
+ if ids:
45
+ criteria["Ids"] = parse_ids(ids)
46
+ if adgroup_ids:
47
+ criteria["AdGroupIds"] = parse_ids(adgroup_ids)
48
+
49
+ params = {"SelectionCriteria": criteria, "FieldNames": field_names}
50
+
51
+ if limit:
52
+ params["Page"] = {"Limit": limit}
53
+
54
+ body = {"method": "get", "params": params}
55
+
56
+ result = client.smartadtargets().post(data=body)
57
+
58
+ if fetch_all:
59
+ items = []
60
+ for item in result().iter_items():
61
+ items.append(item)
62
+ format_output(items, output_format, output)
63
+ else:
64
+ data = result().extract()
65
+ format_output(data, output_format, output)
66
+
67
+ except Exception as e:
68
+ print_error(str(e))
69
+ raise click.Abort()
70
+
71
+
72
+ @smartadtargets.command()
73
+ @click.option("--adgroup-id", required=True, type=int, help="Ad group ID")
74
+ @click.option("--type", "target_type", required=True, help="Target type")
75
+ @click.option("--json", "extra_json", help="Additional JSON parameters")
76
+ @click.option("--dry-run", is_flag=True, help="Show request without sending")
77
+ @click.pass_context
78
+ def add(ctx, adgroup_id, target_type, extra_json, dry_run):
79
+ """Add smart ad target"""
80
+ try:
81
+ target_data = {"AdGroupId": adgroup_id, "Type": target_type}
82
+
83
+ if extra_json:
84
+ extra = json.loads(extra_json)
85
+ target_data.update(extra)
86
+
87
+ body = {"method": "add", "params": {"SmartAdTargets": [target_data]}}
88
+
89
+ if dry_run:
90
+ format_output(body, "json", None)
91
+ return
92
+
93
+ client = create_client(
94
+ token=ctx.obj.get("token"),
95
+ login=ctx.obj.get("login"),
96
+ sandbox=ctx.obj.get("sandbox"),
97
+ )
98
+
99
+ result = client.smartadtargets().post(data=body)
100
+ format_output(result().extract(), "json", None)
101
+
102
+ except Exception as e:
103
+ print_error(str(e))
104
+ raise click.Abort()
@@ -0,0 +1,97 @@
1
+ """
2
+ TurboPages commands
3
+ """
4
+
5
+ import json
6
+ import click
7
+
8
+ from ..api import create_client
9
+ from ..output import format_output, print_error
10
+ from ..utils import parse_ids
11
+
12
+
13
+ @click.group()
14
+ def turbopages():
15
+ """Manage Turbo Pages"""
16
+ pass
17
+
18
+
19
+ @turbopages.command()
20
+ @click.option("--ids", help="Comma-separated Turbo Page IDs")
21
+ @click.option("--limit", type=int, help="Limit number of results")
22
+ @click.option("--fetch-all", is_flag=True, help="Fetch all pages")
23
+ @click.option("--format", "output_format", default="json", help="Output format")
24
+ @click.option("--output", help="Output file")
25
+ @click.option("--fields", help="Comma-separated field names")
26
+ @click.pass_context
27
+ def get(ctx, ids, limit, fetch_all, output_format, output, fields):
28
+ """Get Turbo Pages"""
29
+ try:
30
+ client = create_client(
31
+ token=ctx.obj.get("token"),
32
+ login=ctx.obj.get("login"),
33
+ sandbox=ctx.obj.get("sandbox"),
34
+ )
35
+
36
+ field_names = fields.split(",") if fields else ["Id", "Name", "Status", "Href"]
37
+
38
+ criteria = {}
39
+ if ids:
40
+ criteria["Ids"] = parse_ids(ids)
41
+
42
+ params = {"SelectionCriteria": criteria, "FieldNames": field_names}
43
+
44
+ if limit:
45
+ params["Page"] = {"Limit": limit}
46
+
47
+ body = {"method": "get", "params": params}
48
+
49
+ result = client.turbopages().post(data=body)
50
+
51
+ if fetch_all:
52
+ items = []
53
+ for item in result().iter_items():
54
+ items.append(item)
55
+ format_output(items, output_format, output)
56
+ else:
57
+ data = result().extract()
58
+ format_output(data, output_format, output)
59
+
60
+ except Exception as e:
61
+ print_error(str(e))
62
+ raise click.Abort()
63
+
64
+
65
+ @turbopages.command()
66
+ @click.option("--name", required=True, help="Page name")
67
+ @click.option("--url", required=True, help="Page URL")
68
+ @click.option("--json", "extra_json", help="Additional JSON parameters")
69
+ @click.option("--dry-run", is_flag=True, help="Show request without sending")
70
+ @click.pass_context
71
+ def add(ctx, name, url, extra_json, dry_run):
72
+ """Add Turbo Page"""
73
+ try:
74
+ page_data = {"Name": name, "Href": url}
75
+
76
+ if extra_json:
77
+ extra = json.loads(extra_json)
78
+ page_data.update(extra)
79
+
80
+ body = {"method": "add", "params": {"TurboPages": [page_data]}}
81
+
82
+ if dry_run:
83
+ format_output(body, "json", None)
84
+ return
85
+
86
+ client = create_client(
87
+ token=ctx.obj.get("token"),
88
+ login=ctx.obj.get("login"),
89
+ sandbox=ctx.obj.get("sandbox"),
90
+ )
91
+
92
+ result = client.turbopages().post(data=body)
93
+ format_output(result().extract(), "json", None)
94
+
95
+ except Exception as e:
96
+ print_error(str(e))
97
+ raise click.Abort()
@@ -0,0 +1,93 @@
1
+ """
2
+ VCards commands
3
+ """
4
+
5
+ import json
6
+ import click
7
+
8
+ from ..api import create_client
9
+ from ..output import format_output, print_error
10
+ from ..utils import parse_ids
11
+
12
+
13
+ @click.group()
14
+ def vcards():
15
+ """Manage vCards"""
16
+ pass
17
+
18
+
19
+ @vcards.command()
20
+ @click.option("--ids", help="Comma-separated vCard IDs")
21
+ @click.option("--limit", type=int, help="Limit number of results")
22
+ @click.option("--fetch-all", is_flag=True, help="Fetch all pages")
23
+ @click.option("--format", "output_format", default="json", help="Output format")
24
+ @click.option("--output", help="Output file")
25
+ @click.option("--fields", help="Comma-separated field names")
26
+ @click.pass_context
27
+ def get(ctx, ids, limit, fetch_all, output_format, output, fields):
28
+ """Get vCards"""
29
+ try:
30
+ client = create_client(
31
+ token=ctx.obj.get("token"),
32
+ login=ctx.obj.get("login"),
33
+ sandbox=ctx.obj.get("sandbox"),
34
+ )
35
+
36
+ field_names = (
37
+ fields.split(",")
38
+ if fields
39
+ else ["Id", "CampaignId", "Country", "City", "CompanyName"]
40
+ )
41
+
42
+ criteria = {}
43
+ if ids:
44
+ criteria["Ids"] = parse_ids(ids)
45
+
46
+ params = {"SelectionCriteria": criteria, "FieldNames": field_names}
47
+
48
+ if limit:
49
+ params["Page"] = {"Limit": limit}
50
+
51
+ body = {"method": "get", "params": params}
52
+
53
+ result = client.vcards().post(data=body)
54
+
55
+ if fetch_all:
56
+ items = []
57
+ for item in result().iter_items():
58
+ items.append(item)
59
+ format_output(items, output_format, output)
60
+ else:
61
+ data = result().extract()
62
+ format_output(data, output_format, output)
63
+
64
+ except Exception as e:
65
+ print_error(str(e))
66
+ raise click.Abort()
67
+
68
+
69
+ @vcards.command()
70
+ @click.option("--json", "vcard_json", required=True, help="vCard data in JSON")
71
+ @click.option("--dry-run", is_flag=True, help="Show request without sending")
72
+ @click.pass_context
73
+ def add(ctx, vcard_json, dry_run):
74
+ """Add vCard"""
75
+ try:
76
+ body = {"method": "add", "params": {"VCards": [json.loads(vcard_json)]}}
77
+
78
+ if dry_run:
79
+ format_output(body, "json", None)
80
+ return
81
+
82
+ client = create_client(
83
+ token=ctx.obj.get("token"),
84
+ login=ctx.obj.get("login"),
85
+ sandbox=ctx.obj.get("sandbox"),
86
+ )
87
+
88
+ result = client.vcards().post(data=body)
89
+ format_output(result().extract(), "json", None)
90
+
91
+ except Exception as e:
92
+ print_error(str(e))
93
+ raise click.Abort()
direct_cli/output.py ADDED
@@ -0,0 +1,143 @@
1
+ """
2
+ Output formatting module for Direct CLI
3
+ """
4
+
5
+ import json
6
+ import csv
7
+ import sys
8
+ from typing import Any, List, Dict, Optional
9
+ from io import StringIO
10
+
11
+ try:
12
+ from tabulate import tabulate
13
+ except ImportError:
14
+ tabulate = None
15
+
16
+
17
+ def format_output(
18
+ data: Any,
19
+ format_type: str = "json",
20
+ output_file: Optional[str] = None,
21
+ headers: Optional[List[str]] = None,
22
+ ) -> str:
23
+ """
24
+ Format data for output
25
+
26
+ Args:
27
+ data: Data to format
28
+ format_type: Output format ('json', 'table', 'csv', 'tsv')
29
+ output_file: Output file path (if None, print to stdout)
30
+ headers: Column headers for table/csv/tsv format
31
+
32
+ Returns:
33
+ Formatted string
34
+ """
35
+ if format_type == "json":
36
+ output = format_json(data)
37
+ elif format_type == "table":
38
+ output = format_table(data, headers)
39
+ elif format_type == "csv":
40
+ output = format_csv(data, headers)
41
+ elif format_type == "tsv":
42
+ output = format_tsv(data, headers)
43
+ else:
44
+ output = str(data)
45
+
46
+ if output_file:
47
+ with open(output_file, "w", encoding="utf-8") as f:
48
+ f.write(output)
49
+ else:
50
+ print(output)
51
+
52
+ return output
53
+
54
+
55
+ def format_json(data: Any, indent: int = 2) -> str:
56
+ """Format data as JSON"""
57
+ return json.dumps(data, ensure_ascii=False, indent=indent, default=str)
58
+
59
+
60
+ def format_table(data: Any, headers: Optional[List[str]] = None) -> str:
61
+ """Format data as table"""
62
+ if not tabulate:
63
+ return format_json(data)
64
+
65
+ if isinstance(data, list) and len(data) > 0:
66
+ if isinstance(data[0], dict):
67
+ return tabulate(data, headers="keys", tablefmt="grid")
68
+ elif isinstance(data[0], list):
69
+ return tabulate(data, headers=headers or [], tablefmt="grid")
70
+ elif isinstance(data, dict):
71
+ # Convert dict to list of lists for table display
72
+ rows = [[k, v] for k, v in data.items()]
73
+ return tabulate(rows, headers=["Key", "Value"], tablefmt="grid")
74
+
75
+ return str(data)
76
+
77
+
78
+ def format_csv(data: Any, headers: Optional[List[str]] = None) -> str:
79
+ """Format data as CSV"""
80
+ output = StringIO()
81
+
82
+ if isinstance(data, list) and len(data) > 0:
83
+ if isinstance(data[0], dict):
84
+ fieldnames = headers or list(data[0].keys())
85
+ writer = csv.DictWriter(output, fieldnames=fieldnames)
86
+ writer.writeheader()
87
+ writer.writerows(data)
88
+ elif isinstance(data[0], list):
89
+ writer = csv.writer(output)
90
+ if headers:
91
+ writer.writerow(headers)
92
+ writer.writerows(data)
93
+ elif isinstance(data, dict):
94
+ writer = csv.writer(output)
95
+ writer.writerow(["Key", "Value"])
96
+ for k, v in data.items():
97
+ writer.writerow([k, v])
98
+
99
+ return output.getvalue()
100
+
101
+
102
+ def format_tsv(data: Any, headers: Optional[List[str]] = None) -> str:
103
+ """Format data as TSV"""
104
+ output = StringIO()
105
+
106
+ if isinstance(data, list) and len(data) > 0:
107
+ if isinstance(data[0], dict):
108
+ fieldnames = headers or list(data[0].keys())
109
+ writer = csv.DictWriter(output, fieldnames=fieldnames, delimiter="\t")
110
+ writer.writeheader()
111
+ writer.writerows(data)
112
+ elif isinstance(data[0], list):
113
+ writer = csv.writer(output, delimiter="\t")
114
+ if headers:
115
+ writer.writerow(headers)
116
+ writer.writerows(data)
117
+ elif isinstance(data, dict):
118
+ writer = csv.writer(output, delimiter="\t")
119
+ writer.writerow(["Key", "Value"])
120
+ for k, v in data.items():
121
+ writer.writerow([k, v])
122
+
123
+ return output.getvalue()
124
+
125
+
126
+ def print_success(message: str) -> None:
127
+ """Print success message"""
128
+ print(f"\033[32m✓ {message}\033[0m")
129
+
130
+
131
+ def print_error(message: str) -> None:
132
+ """Print error message"""
133
+ print(f"\033[31m✗ {message}\033[0m", file=sys.stderr)
134
+
135
+
136
+ def print_warning(message: str) -> None:
137
+ """Print warning message"""
138
+ print(f"\033[33m⚠ {message}\033[0m")
139
+
140
+
141
+ def print_info(message: str) -> None:
142
+ """Print info message"""
143
+ print(f"ℹ {message}")