Merge pull request #275 from iGodly/feat/mailchimp-cli-harness

feat: add Mailchimp CLI harness
This commit is contained in:
Yuhao
2026-05-13 16:28:24 +08:00
committed by GitHub
53 changed files with 11071 additions and 0 deletions
+4
View File
@@ -87,6 +87,7 @@
!/macrocli/
!/sbox/
!/quietshrink/
!/mailchimp/
# Step 5: Inside each software dir, ignore everything (including dotfiles)
/gimp/*
@@ -181,6 +182,8 @@
/sbox/.*
/quietshrink/*
/quietshrink/.*
/mailchimp/*
/mailchimp/.*
# Step 6: ...except agent-harness/
!/gimp/agent-harness/
@@ -235,6 +238,7 @@
!/macrocli/agent-harness/
!/sbox/agent-harness/
!/quietshrink/agent-harness/
!/mailchimp/agent-harness/
# Exclude non-gedit demo macros from macrocli (local only)
/macrocli/agent-harness/cli_anything/macrocli/macro_definitions/demo/flameshot*
+8
View File
@@ -862,6 +862,13 @@ Each application received complete, production-ready CLI interfaces — not demo
<td align="center">✅ <a href="n8n/agent-harness/">55+ cmds</a></td>
</tr>
<tr>
<td align="center"><strong>📧 <a href="mailchimp/agent-harness/">Mailchimp</a></strong></td>
<td>Email Marketing &amp; Automation</td>
<td><code>cli-anything-mailchimp</code></td>
<td>Mailchimp Marketing API v3.0</td>
<td align="center">✅ <a href="mailchimp/agent-harness/">303 cmds</a></td>
</tr>
<tr>
<td align="center"><strong>📚 <a href="zotero/agent-harness/">Zotero</a></strong></td>
<td>Reference Management</td>
<td><code>cli-anything-zotero</code></td>
@@ -1162,6 +1169,7 @@ cli-anything/
├── 🎵 audacity/agent-harness/ # Audacity CLI (161 tests)
├── 🌐 browser/agent-harness/ # Browser CLI (DOMShell MCP, new)
├── 📄 libreoffice/agent-harness/ # LibreOffice CLI (158 tests)
├── 📧 mailchimp/agent-harness/ # Mailchimp Marketing API CLI (303 commands, 36 unit tests)
├── 📚 zotero/agent-harness/ # Zotero CLI (new, write import support)
├── 📝 mubu/agent-harness/ # Mubu CLI (96 tests)
├── 📹 obs-studio/agent-harness/ # OBS Studio CLI (153 tests)
+160
View File
@@ -0,0 +1,160 @@
# MAILCHIMP.md — cli-anything Harness for Mailchimp Marketing API v3.0
## Overview
`cli-anything-mailchimp` makes the [Mailchimp Marketing API v3.0](https://mailchimp.com/developer/marketing/docs/fundamentals/) fully agent-native. Every endpoint from the official Swagger spec is exposed as a typed Click command, with JSON output and REPL mode.
**303 commands across 30 resource groups**, covering the full Marketing API surface.
---
## Backend
| Property | Value |
|---|---|
| API base | `https://<dc>.api.mailchimp.com/3.0` |
| Auth | HTTP Basic — username `anystring`, password = API key |
| Key format | `<random>-<datacenter>` e.g. `abc123-us8` |
| Spec source | `mailchimp/mailchimp-client-lib-codegen` → `spec/marketing.json` (Swagger 2.0) |
**Required env var:**
```bash
export MAILCHIMP_API_KEY=<your-key>-<datacenter>
```
No config files. The data-centre prefix (`us8`, `eu2`, etc.) is derived automatically from the API key suffix.
---
## Command Hierarchy
```
cli-anything-mailchimp [--json] [--version]
│
├── ping # GET /ping — health check
├── root # GET / — account info
│
├── lists # 66 operations (audiences, members, merge fields, segments, tags, webhooks)
│ ├── list / get / create / update / delete
│ ├── list-lists-id-members / get-lists-id-members-id / create-members / ...
│ ├── list-merge-fields / create-merge-fields / ...
│ ├── list-segments / get-segment-id / create-segments / ...
│ └── list-webhooks / get-webhook-id / create-webhooks / ...
│
├── campaigns # 22 operations
│ ├── list / get / create / update / delete
│ ├── send / schedule / cancel-send / pause / resume / replicate
│ ├── list-content / list-feedback / get-feedback-id / create-feedback
│ └── list-send-checklist / create-resend
│
├── reports # 22 operations — sent campaign analytics
│ ├── list / get
│ ├── list-abuse-reports / list-click-details / list-domain-performance
│ ├── list-email-activity / list-locations / list-open-details
│ └── list-sub-reports / list-unsubscribed
│
├── automations # 18 operations
│ ├── list / get / create / update
│ ├── pause / start / archive
│ └── list-emails / get-email / pause-email / start-email / delete-email
│
├── ecommerce # 60 operations — stores, orders, products, carts, promo codes
├── templates # 6 operations
├── template-folders # 5 operations
├── campaign-folders # 5 operations
├── file-manager # 11 operations — files and folders
├── reporting # 12 operations — Facebook/landing-page reporting
├── landing-pages # 8 operations
├── sms-campaigns # 10 operations
├── surveys # 3 operations
├── audiences # 4 operations — connected audience management
├── batch-webhooks # 5 operations
├── batches # 4 operations — bulk API batch operations
├── connected-sites # 5 operations
├── contacts # 4 operations
├── conversations # 4 operations
├── customer-journeys # 1 operation
├── facebook-ads # 2 operations
├── verified-domains # 5 operations
├── authorized-apps # 2 operations
├── activity-feed # 1 operation
├── account-export # 1 operation
├── account-exports # 2 operations
├── search-campaigns # 1 operation
└── search-members # 1 operation
```
---
## Output Strategy
All commands share a root `--json` flag:
```bash
cli-anything-mailchimp --json lists list
cli-anything-mailchimp --json campaigns get <campaign_id>
```
**JSON envelopes** (commands emit the native Mailchimp API response):
| Endpoint type | Shape |
|---|---|
| List / collection | Native shape: `{"lists":[...], "total_items":N}` / `{"campaigns":[...]}` etc. — resource key matches the API path noun |
| Single resource GET | Raw Mailchimp response object |
| Mutation (POST/PATCH/PUT) | Raw Mailchimp response object |
| DELETE | `{"ok": true, "message": "Deleted."}` |
| Error | `{"ok": false, "message": "...", "data": <mailchimp-problem-detail>}` |
Use the Mailchimp-native field name when piping to `jq` (e.g. `.lists[]`, `.campaigns[]`, `.members[]`).
Human mode prints compact key-value pairs for single objects and annotated lists for collections.
Collection commands expose Mailchimp's native `count` and `offset` query parameters where the API supports them. The CLI does not auto-fetch every page by default.
---
## Design Decisions
1. **Code generation from spec** — 303 commands are auto-generated from Mailchimp's public Swagger 2.0 spec via `_codegen/generate.py`. Generated files are committed so end-users get fast `--help` without downloading the spec.
2. **Env-var-only auth** — per CLI-Anything convention, no config files. `MAILCHIMP_API_KEY` is the single source of truth.
3. **Verbatim `repl_skin.py`** — the REPL skin is copied unmodified from `cli-anything-plugin/repl_skin.py` as required by CLI-Anything's contribution rules.
4. **Path params as positional args** — Mailchimp path parameters (`{list_id}`, `{campaign_id}`, etc.) become required positional Click arguments, keeping commands concise.
5. **Body as `--data` JSON** — request bodies are passed as `--data '{"key": "value"}'` JSON strings. This avoids generating dozens of per-field flags per command and works naturally for agent use (agents can construct JSON payloads directly).
6. **Subscriber hash utility** — `cli_anything.mailchimp.core.client.subscriber_hash(email)` computes the MD5 hash Mailchimp uses as a member identifier, matching the Node.js reference implementation.
---
## File Layout
```
mailchimp/agent-harness/
├── setup.py
├── MAILCHIMP.md ← this file
└── cli_anything/mailchimp/
├── __init__.py
├── __main__.py
├── mailchimp_cli.py ← Click root + REPL
├── README.md
├── core/
│ ├── client.py ← HTTP client, auth, error handling
│ └── pagination.py ← count/offset paginator
├── commands/ ← auto-generated (30 modules)
│ ├── __init__.py ← imports ALL_GROUPS
│ ├── lists.py ← 66 commands
│ ├── campaigns.py ← 22 commands
│ └── ... ← one module per tag
├── utils/
│ ├── output.py ← _out() JSON/human switch
│ └── repl_skin.py ← verbatim copy from cli-anything-plugin/repl_skin.py
├── _codegen/
│ └── generate.py ← spec → commands/*.py generator
├── skills/SKILL.md ← packaged skill copy
└── tests/
├── TEST.md
├── test_core.py ← unit tests (no API key)
└── test_full_e2e.py ← 9 live tests (gated on API key)
```
@@ -0,0 +1,26 @@
# cli-anything-mailchimp
Python CLI harness for the [Mailchimp Marketing API v3.0](https://mailchimp.com/developer/marketing/docs/fundamentals/), built on the [CLI-Anything](https://github.com/HKUDS/CLI-Anything) framework.
## Install
```bash
pip install git+https://github.com/HKUDS/CLI-Anything.git#subdirectory=mailchimp/agent-harness
```
## Auth
```bash
export MAILCHIMP_API_KEY=<your-api-key>-<datacenter>
```
## Usage
```bash
cli-anything-mailchimp ping
cli-anything-mailchimp --json lists list
cli-anything-mailchimp --json campaigns list --count 10
cli-anything-mailchimp # interactive REPL
```
See `SKILL.md` for full command reference.
@@ -0,0 +1 @@
@@ -0,0 +1,3 @@
from cli_anything.mailchimp.mailchimp_cli import main
if __name__ == "__main__":
main()
@@ -0,0 +1,429 @@
"""
Code generator: Mailchimp Swagger 2.0 spec → Click command modules.
Usage:
python -m cli_anything.mailchimp._codegen.generate
Downloads the Mailchimp Marketing API spec and emits one Python module per
tag into cli_anything/mailchimp/commands/. The generated files are committed
to the repository so end-users do not need to run the generator.
"""
from __future__ import annotations
import builtins
import json
import keyword
import os
import re
import sys
import textwrap
from collections import defaultdict
from pathlib import Path
import requests
SPEC_URL = (
"https://raw.githubusercontent.com/mailchimp/mailchimp-client-lib-codegen"
"/main/spec/marketing.json"
)
OUT_DIR = Path(__file__).resolve().parents[1] / "commands"
# Tags that should be skipped (internal/meta).
_SKIP_TAGS: set[str] = set()
# Names that must not appear as Python function/param names even if technically
# valid identifiers (builtins + common shadowing traps).
_BUILTIN_NAMES: frozenset[str] = frozenset(dir(builtins))
def _slugify(name: str) -> str:
"""Convert camelCase or PascalCase or space-separated to kebab-case."""
s = re.sub(r"([A-Z]+)([A-Z][a-z])", r"\1-\2", name)
s = re.sub(r"([a-z\d])([A-Z])", r"\1-\2", s)
s = s.replace(" ", "-").replace("_", "-").lower()
s = re.sub(r"-+", "-", s).strip("-")
return s
def _safe_name(name: str) -> str:
"""Convert a string to a safe Python identifier (keywords and builtins get underscore suffix)."""
name = re.sub(r"[^a-zA-Z0-9_]", "_", name)
if name and name[0].isdigit():
name = "_" + name
if keyword.iskeyword(name) or name in _BUILTIN_NAMES:
name += "_"
return name
def _tag_to_module(tag: str) -> str:
return _slugify(tag).replace("-", "_")
def _tag_to_group(tag: str) -> str:
return _slugify(tag)
def _infer_command_name(method: str, path: str, operation_id: str) -> str:
"""Derive a clean command name from the HTTP method and path."""
# Action endpoints (POST to .../actions/xxx)
action_match = re.search(r"/actions/([^/]+)$", path)
if action_match:
return _slugify(action_match.group(1))
# Determine if this is a collection endpoint or single-resource by checking
# whether the path ends with a path parameter.
ends_with_param = bool(re.search(r"\{[^}]+\}$", path))
if method == "get":
return "get" if ends_with_param else "list"
elif method == "post":
return "create"
elif method in ("patch", "put"):
return "update"
elif method == "delete":
return "delete"
else:
return _slugify(operation_id)
def _dedup_suffix(cmd_name: str, path: str, operation_id: str, count: int) -> str:
"""Return a unique suffix for a colliding command name.
Prefers a slug derived from the operationId over a raw path segment,
because path segments are often path-param shaped ({list_id}) which
produces confusing names like 'create-list-id'.
"""
# Try a meaningful suffix from the operationId (strip the HTTP verb prefix)
op_slug = _slugify(re.sub(r"^(get|post|patch|put|delete)", "", operation_id))
if op_slug and op_slug != cmd_name:
return f"{cmd_name}-{op_slug}"
# Fall back to the last non-param path segment
segments = [s for s in path.rstrip("/").split("/") if s and not s.startswith("{")]
if segments:
suffix = _slugify(segments[-1])
if suffix and suffix != cmd_name:
return f"{cmd_name}-{suffix}"
return f"{cmd_name}-{count}"
def _param_type_to_click(param: dict) -> str:
t = param.get("type", "string")
if t == "integer":
return "int"
if t == "boolean":
return "bool"
return "str"
def _click_help_text(description: str) -> str:
"""Return a single-line Click help string without truncating useful API context."""
first_line = description.split("\n")[0]
return " ".join(first_line.replace('"', '\\"').split())
def _collect_params(operation: dict, path: str) -> tuple[list[dict], list[dict], dict | None]:
"""Return (path_params, query_params, body_param) from operation."""
params = operation.get("parameters", [])
path_params = [p for p in params if p.get("in") == "path"]
query_params = [p for p in params if p.get("in") == "query"]
body_params = [p for p in params if p.get("in") == "body"]
body = body_params[0] if body_params else None
return path_params, query_params, body
def _generate_command_func(
cmd_name: str,
method: str,
path: str,
operation: dict,
path_params: list,
query_params: list,
body_param: dict | None,
) -> str:
summary = operation.get("summary", "").replace('"', '\\"')
# C1: prefix with _cmd_ so no builtin shadowing (list, get, delete, etc.)
func_name = "_cmd_" + _safe_name(cmd_name.replace("-", "_"))
lines: list[str] = []
group_var = f"{_tag_to_module(operation['tags'][0])}_group"
decorators = [f'@{group_var}.command("{cmd_name}")']
# Path params as Click arguments (positional)
for p in path_params:
py_arg = _safe_name(p["name"]).upper()
decorators.append(f'@click.argument("{py_arg}")')
# Body option
if body_param is not None:
decorators.append(
'@click.option("--data", default=None, '
'help="Request body as JSON string.")'
)
# I3: All query params (no arbitrary cap); --extra-params escape hatch
for p in query_params:
flag = _slugify(p["name"])
py_name = _safe_name(p["name"]).lower()
ptype = _param_type_to_click(p)
desc = _click_help_text(p.get("description", ""))
if ptype == "int":
type_clause = "type=int, "
elif ptype == "bool":
type_clause = "type=bool, "
else:
type_clause = ""
decorators.append(
f'@click.option("--{flag}", "{py_name}", default=None, {type_clause}help="{desc}")'
)
# Escape hatch for extra/future query params
decorators.append(
'@click.option("--extra-params", default=None, '
'help="Extra query params as JSON object, e.g. \'{\\"key\\":\\"val\\"}\'")'
)
decorators.append("@click.pass_context")
for d in decorators:
lines.append(d)
# Function signature — M4: no module-level USE_JSON import needed
sig_parts = ["ctx"]
for p in path_params:
sig_parts.append(_safe_name(p["name"]).lower())
if body_param is not None:
sig_parts.append("data")
for p in query_params:
sig_parts.append(_safe_name(p["name"]).lower())
sig_parts.append("extra_params")
lines.append(f'def {func_name}({", ".join(sig_parts)}):')
lines.append(f' """{summary}"""')
lines.append(" from cli_anything.mailchimp.core.client import get_client, MailchimpError")
lines.append(" from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err")
# Build path with lowercased path params
url_parts = re.sub(
r"\{([^}]+)\}",
lambda m: "{" + _safe_name(m.group(1)).lower() + "}",
path,
)
lines.append(f' path = f"{url_parts}"')
# Build query params dict
if query_params:
lines.append(" params = {k: v for k, v in {")
for p in query_params:
py_name = _safe_name(p["name"]).lower()
lines.append(f' "{p["name"]}": {py_name},')
lines.append(" }.items() if v is not None}")
else:
lines.append(" params = {}")
# Merge --extra-params
lines.append(" if extra_params:")
lines.append(' params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))')
# Parse body
if body_param is not None:
lines.append(' body = _parse_json_option(data, "--data") if data else None')
# Make the request
lines.append(" client = get_client()")
lines.append(" try:")
if method == "get":
lines.append(" result = client.get(path, params=params or None)")
elif method == "post":
body_arg = "body" if body_param else "None"
lines.append(f" result = client.post(path, json={body_arg}, params=params or None)")
elif method == "patch":
body_arg = "body" if body_param else "None"
lines.append(f" result = client.patch(path, json={body_arg}, params=params or None)")
elif method == "put":
body_arg = "body" if body_param else "None"
lines.append(f" result = client.put(path, json={body_arg}, params=params or None)")
elif method == "delete":
lines.append(" result = client.delete(path, params=params or None)")
else:
lines.append(" result = client.get(path, params=params or None)")
lines.append(" except MailchimpError as e:")
lines.append(" _out_err(e.status, e.title, e.detail, e.raw)")
lines.append(" return")
if method == "delete":
lines.append(' _out_ok("Deleted.")')
else:
lines.append(" _out(result)")
lines.append("")
return "\n".join(lines)
def _generate_module(tag: str, operations: list[tuple[str, str, dict]]) -> str:
"""Generate a complete Python module for a given tag."""
module_name = _tag_to_module(tag)
group_name = _tag_to_group(tag)
group_var = f"{module_name}_group"
group_decorator = f'@click.group("{group_name}")'
group_signature = f"def {group_var}(ctx):"
if tag.lower() == "ping":
group_decorator = f'@click.group("{group_name}", invoke_without_command=True)'
group_lines = [
f' """{_slugify(tag)} resource commands."""',
]
if tag.lower() == "ping":
group_lines.extend(
[
" if ctx.invoked_subcommand is None:",
" ctx.invoke(_cmd_list_)",
]
)
header_lines = [
f'"""Commands for the Mailchimp /{group_name} API resource.',
"",
"Auto-generated by cli_anything/mailchimp/_codegen/generate.py.",
"Do not edit manually — re-run the generator to update.",
'"""',
"",
"import json",
"",
"import click",
"",
"",
"def _parse_json_option(value, option_name, require_object=False):",
" try:",
" parsed = json.loads(value)",
" except json.JSONDecodeError as exc:",
" raise click.BadParameter(",
' f"must be valid JSON ({exc.msg})",',
" param_hint=option_name,",
" ) from exc",
" if require_object and not isinstance(parsed, dict):",
' raise click.BadParameter("must be a JSON object", param_hint=option_name)',
" return parsed",
"",
"",
group_decorator,
"@click.pass_context",
group_signature,
*group_lines,
"",
"",
]
header = "\n".join(header_lines)
body_parts: list[str] = []
# Track ALL assigned names (base + deduped) to guarantee uniqueness.
assigned: set[str] = set()
for method, path, operation in sorted(operations, key=lambda x: (x[1], x[0])):
op_id = operation.get("operationId", "")
cmd_name = _infer_command_name(method, path, op_id)
# I4: De-duplicate using operationId slug; fall back to counter if still collides.
if cmd_name in assigned:
candidate = _dedup_suffix(cmd_name, path, op_id, 2)
counter = 2
while candidate in assigned:
counter += 1
candidate = f"{cmd_name}-{counter}"
cmd_name = candidate
assigned.add(cmd_name)
operation = dict(operation)
operation.setdefault("tags", [tag])
path_params, query_params, body_param = _collect_params(operation, path)
func_code = _generate_command_func(
cmd_name, method, path, operation, path_params, query_params, body_param
)
body_parts.append(func_code)
if tag.lower() == "campaigns" and cmd_name == "list-campaigns-id-content":
body_parts.append('campaigns_group.add_command(_cmd_list_campaigns_id_content, "list-content")\n')
if tag.lower() == "campaigns" and cmd_name == "list-campaigns-id-send-checklist":
body_parts.append('campaigns_group.add_command(_cmd_list_campaigns_id_send_checklist, "list-send-checklist")\n')
if tag.lower() == "reports" and cmd_name == "list-reports-id-click-details":
body_parts.append('reports_group.add_command(_cmd_list_reports_id_click_details, "list-click-details")\n')
if tag.lower() == "reports" and cmd_name == "list-reports-id-domain-performance":
body_parts.append('reports_group.add_command(_cmd_list_reports_id_domain_performance, "list-domain-performance")\n')
if tag.lower() == "reports" and cmd_name == "list-reports-id-email-activity":
body_parts.append('reports_group.add_command(_cmd_list_reports_id_email_activity, "list-email-activity")\n')
if tag.lower() == "reports" and cmd_name == "list-reports-id-locations":
body_parts.append('reports_group.add_command(_cmd_list_reports_id_locations, "list-locations")\n')
if tag.lower() == "reports" and cmd_name == "list-reports-id-open-details":
body_parts.append('reports_group.add_command(_cmd_list_reports_id_open_details, "list-open-details")\n')
if tag.lower() == "reports" and cmd_name == "list-reports-id-unsubscribed":
body_parts.append('reports_group.add_command(_cmd_list_reports_id_unsubscribed, "list-unsubscribed")\n')
if tag.lower() == "automations" and cmd_name == "list-automations-id-emails":
body_parts.append('automations_group.add_command(_cmd_list_automations_id_emails, "list-emails")\n')
if tag.lower() == "lists" and cmd_name == "create-lists-id-members":
body_parts.append('lists_group.add_command(_cmd_create_lists_id_members, "create-members")\n')
if tag.lower() == "lists" and cmd_name == "list-preview-a-segment":
body_parts.append('lists_group.add_command(_cmd_list_preview_a_segment, "list-lists-id-segments")\n')
return header + "\n".join(body_parts)
def load_spec(url: str = SPEC_URL) -> dict:
print(f"Fetching spec from {url} ...", file=sys.stderr)
resp = requests.get(url, timeout=60)
resp.raise_for_status()
return resp.json()
def generate(spec: dict, out_dir: Path = OUT_DIR) -> None:
out_dir.mkdir(parents=True, exist_ok=True)
by_tag: dict[str, list[tuple[str, str, dict]]] = defaultdict(list)
for path, path_item in spec.get("paths", {}).items():
for method, operation in path_item.items():
if method not in ("get", "post", "patch", "put", "delete"):
continue
if not isinstance(operation, dict):
continue
tags = operation.get("tags", ["misc"])
tag = tags[0]
if tag in _SKIP_TAGS:
continue
by_tag[tag].append((method, path, operation))
generated: list[str] = []
for tag, operations in sorted(by_tag.items()):
module_name = _tag_to_module(tag)
out_path = out_dir / f"{module_name}.py"
code = _generate_module(tag, operations)
out_path.write_text(code, encoding="utf-8")
generated.append(module_name)
print(f" Generated {out_path.name} ({len(operations)} commands)", file=sys.stderr)
# Write __init__.py that imports all groups
init_lines = ['"""Auto-generated command groups for cli-anything-mailchimp."""', ""]
for mod in sorted(generated):
group_var = f"{mod}_group"
init_lines.append(f"from cli_anything.mailchimp.commands.{mod} import {group_var}")
init_lines.append("")
init_lines.append("ALL_GROUPS = [")
for mod in sorted(generated):
group_var = f"{mod}_group"
init_lines.append(f" {group_var},")
init_lines.append("]")
init_lines.append("")
(out_dir / "__init__.py").write_text("\n".join(init_lines), encoding="utf-8")
print(f"\nDone — {len(generated)} modules written to {out_dir}", file=sys.stderr)
if __name__ == "__main__":
spec = load_spec()
generate(spec)
@@ -0,0 +1,65 @@
"""Auto-generated command groups for cli-anything-mailchimp."""
from cli_anything.mailchimp.commands.account_export import account_export_group
from cli_anything.mailchimp.commands.account_exports import account_exports_group
from cli_anything.mailchimp.commands.activity_feed import activity_feed_group
from cli_anything.mailchimp.commands.audiences import audiences_group
from cli_anything.mailchimp.commands.authorized_apps import authorized_apps_group
from cli_anything.mailchimp.commands.automations import automations_group
from cli_anything.mailchimp.commands.batch_webhooks import batch_webhooks_group
from cli_anything.mailchimp.commands.batches import batches_group
from cli_anything.mailchimp.commands.campaign_folders import campaign_folders_group
from cli_anything.mailchimp.commands.campaigns import campaigns_group
from cli_anything.mailchimp.commands.connected_sites import connected_sites_group
from cli_anything.mailchimp.commands.contacts import contacts_group
from cli_anything.mailchimp.commands.conversations import conversations_group
from cli_anything.mailchimp.commands.customer_journeys import customer_journeys_group
from cli_anything.mailchimp.commands.ecommerce import ecommerce_group
from cli_anything.mailchimp.commands.facebook_ads import facebook_ads_group
from cli_anything.mailchimp.commands.file_manager import file_manager_group
from cli_anything.mailchimp.commands.landing_pages import landing_pages_group
from cli_anything.mailchimp.commands.lists import lists_group
from cli_anything.mailchimp.commands.ping import ping_group
from cli_anything.mailchimp.commands.reporting import reporting_group
from cli_anything.mailchimp.commands.reports import reports_group
from cli_anything.mailchimp.commands.root import root_group
from cli_anything.mailchimp.commands.search_campaigns import search_campaigns_group
from cli_anything.mailchimp.commands.search_members import search_members_group
from cli_anything.mailchimp.commands.sms_campaigns import sms_campaigns_group
from cli_anything.mailchimp.commands.surveys import surveys_group
from cli_anything.mailchimp.commands.template_folders import template_folders_group
from cli_anything.mailchimp.commands.templates import templates_group
from cli_anything.mailchimp.commands.verified_domains import verified_domains_group
ALL_GROUPS = [
account_export_group,
account_exports_group,
activity_feed_group,
audiences_group,
authorized_apps_group,
automations_group,
batch_webhooks_group,
batches_group,
campaign_folders_group,
campaigns_group,
connected_sites_group,
contacts_group,
conversations_group,
customer_journeys_group,
ecommerce_group,
facebook_ads_group,
file_manager_group,
landing_pages_group,
lists_group,
ping_group,
reporting_group,
reports_group,
root_group,
search_campaigns_group,
search_members_group,
sms_campaigns_group,
surveys_group,
template_folders_group,
templates_group,
verified_domains_group,
]
@@ -0,0 +1,53 @@
"""Commands for the Mailchimp /account-export API resource.
Auto-generated by cli_anything/mailchimp/_codegen/generate.py.
Do not edit manually — re-run the generator to update.
"""
import json
import click
def _parse_json_option(value, option_name, require_object=False):
try:
parsed = json.loads(value)
except json.JSONDecodeError as exc:
raise click.BadParameter(
f"must be valid JSON ({exc.msg})",
param_hint=option_name,
) from exc
if require_object and not isinstance(parsed, dict):
raise click.BadParameter("must be a JSON object", param_hint=option_name)
return parsed
@click.group("account-export")
@click.pass_context
def account_export_group(ctx):
"""account-export resource commands."""
@account_export_group.command("get")
@click.argument("EXPORT_ID")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_get(ctx, export_id, fields, exclude_fields, extra_params):
"""Get account export info"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/account-exports/{export_id}"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@@ -0,0 +1,77 @@
"""Commands for the Mailchimp /account-exports API resource.
Auto-generated by cli_anything/mailchimp/_codegen/generate.py.
Do not edit manually — re-run the generator to update.
"""
import json
import click
def _parse_json_option(value, option_name, require_object=False):
try:
parsed = json.loads(value)
except json.JSONDecodeError as exc:
raise click.BadParameter(
f"must be valid JSON ({exc.msg})",
param_hint=option_name,
) from exc
if require_object and not isinstance(parsed, dict):
raise click.BadParameter("must be a JSON object", param_hint=option_name)
return parsed
@click.group("account-exports")
@click.pass_context
def account_exports_group(ctx):
"""account-exports resource commands."""
@account_exports_group.command("list")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--count", "count", default=None, type=int, help="The number of records to return. Default value is 10. Maximum value is 1000")
@click.option("--offset", "offset", default=None, type=int, help="Used for [pagination](https://mailchimp.com/developer/marketing/docs/methods-parameters/#pagination), this is the number of records from a collection to skip. Default value is 0.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_list_(ctx, fields, exclude_fields, count, offset, extra_params):
"""List account exports"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/account-exports"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
"count": count,
"offset": offset,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@account_exports_group.command("create")
@click.option("--data", default=None, help="Request body as JSON string.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_create(ctx, data, extra_params):
"""Add export"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/account-exports"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
body = _parse_json_option(data, "--data") if data else None
client = get_client()
try:
result = client.post(path, json=body, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@@ -0,0 +1,52 @@
"""Commands for the Mailchimp /activity-feed API resource.
Auto-generated by cli_anything/mailchimp/_codegen/generate.py.
Do not edit manually — re-run the generator to update.
"""
import json
import click
def _parse_json_option(value, option_name, require_object=False):
try:
parsed = json.loads(value)
except json.JSONDecodeError as exc:
raise click.BadParameter(
f"must be valid JSON ({exc.msg})",
param_hint=option_name,
) from exc
if require_object and not isinstance(parsed, dict):
raise click.BadParameter("must be a JSON object", param_hint=option_name)
return parsed
@click.group("activity-feed")
@click.pass_context
def activity_feed_group(ctx):
"""activity-feed resource commands."""
@activity_feed_group.command("list")
@click.option("--count", "count", default=None, type=int, help="The number of records to return. Default value is 10. Maximum value is 1000")
@click.option("--offset", "offset", default=None, type=int, help="Used for [pagination](https://mailchimp.com/developer/marketing/docs/methods-parameters/#pagination), this is the number of records from a collection to skip. Default value is 0.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_list_(ctx, count, offset, extra_params):
"""Get latest chimp chatter"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/activity-feed/chimp-chatter"
params = {k: v for k, v in {
"count": count,
"offset": offset,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@@ -0,0 +1,150 @@
"""Commands for the Mailchimp /audiences API resource.
Auto-generated by cli_anything/mailchimp/_codegen/generate.py.
Do not edit manually — re-run the generator to update.
"""
import json
import click
def _parse_json_option(value, option_name, require_object=False):
try:
parsed = json.loads(value)
except json.JSONDecodeError as exc:
raise click.BadParameter(
f"must be valid JSON ({exc.msg})",
param_hint=option_name,
) from exc
if require_object and not isinstance(parsed, dict):
raise click.BadParameter("must be a JSON object", param_hint=option_name)
return parsed
@click.group("audiences")
@click.pass_context
def audiences_group(ctx):
"""audiences resource commands."""
@audiences_group.command("list")
@click.argument("AUDIENCE_ID")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--count", "count", default=None, type=int, help="The number of records to return. Default value is 10. Maximum value is 1000")
@click.option("--cursor", "cursor", default=None, help="Paginate through a collection of records by setting the `cursor` parameter to a `next_cursor` attribute returned by a previous request. Default value fetches the first \"page\" of results.")
@click.option("--created-before", "created_before", default=None, help="Restricts the response to contacts created at or before the specified time (inclusive). Uses ISO 8601 format: 2025-04-23T15:41:36+00:00.")
@click.option("--created-since", "created_since", default=None, help="Restricts the response to contacts created after the specified time (exclusive). Uses ISO 8601 format: 2025-04-23T15:41:36+00:00.")
@click.option("--updated-before", "updated_before", default=None, help="Restricts the response to contacts updated at or before the specified time (inclusive). Uses ISO 8601 format: 2025-04-23T15:41:36+00:00.")
@click.option("--updated-since", "updated_since", default=None, help="Restricts the response to contacts updated after the specified time (exclusive). Uses ISO 8601 format: 2025-04-23T15:41:36+00:00.")
@click.option("--sort-field", "sort_field", default=None, help="Specifies the field to sort the returned contacts by.")
@click.option("--sort-dir", "sort_dir", default=None, help="Determines the order direction for sorted results.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_list_(ctx, audience_id, fields, exclude_fields, count, cursor, created_before, created_since, updated_before, updated_since, sort_field, sort_dir, extra_params):
"""Get Contacts"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/audiences/{audience_id}/contacts"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
"count": count,
"cursor": cursor,
"created_before": created_before,
"created_since": created_since,
"updated_before": updated_before,
"updated_since": updated_since,
"sort_field": sort_field,
"sort_dir": sort_dir,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@audiences_group.command("create")
@click.argument("AUDIENCE_ID")
@click.option("--data", default=None, help="Request body as JSON string.")
@click.option("--merge-field-validation-mode", "merge_field_validation_mode", default=None, help="Defines how merge field validation is handled. When set to `ignore_required_checks`, the API does not raise an error if required merge fields are missing from the request. When set to `strict`, the API enforces validation and returns an error if any required merge field is not provided. If this setting is omitted, `strict` is applied by default.")
@click.option("--data-mode", "data_mode", default=None, help="Indicates the data processing mode. In `historical` mode, contact data changes do not trigger automations or webhooks. In `live mode`, such changes do trigger them.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_create(ctx, audience_id, data, merge_field_validation_mode, data_mode, extra_params):
"""Add Contact"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/audiences/{audience_id}/contacts"
params = {k: v for k, v in {
"merge_field_validation_mode": merge_field_validation_mode,
"data_mode": data_mode,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
body = _parse_json_option(data, "--data") if data else None
client = get_client()
try:
result = client.post(path, json=body, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@audiences_group.command("get")
@click.argument("AUDIENCE_ID")
@click.argument("CONTACT_ID")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_get(ctx, audience_id, contact_id, fields, exclude_fields, extra_params):
"""Get Contact"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/audiences/{audience_id}/contacts/{contact_id}"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@audiences_group.command("update")
@click.argument("AUDIENCE_ID")
@click.argument("CONTACT_ID")
@click.option("--data", default=None, help="Request body as JSON string.")
@click.option("--merge-field-validation-mode", "merge_field_validation_mode", default=None, help="Defines how merge field validation is handled. When set to `ignore_required_checks`, the API does not raise an error if required merge fields are missing from the request. When set to `strict`, the API enforces validation and returns an error if any required merge field is not provided. If this setting is omitted, `strict` is applied by default.")
@click.option("--data-mode", "data_mode", default=None, help="Indicates the data processing mode. In `historical` mode, contact data changes do not trigger automations or webhooks. In `live mode`, such changes do trigger them.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_update(ctx, audience_id, contact_id, data, merge_field_validation_mode, data_mode, extra_params):
"""Update Contact"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/audiences/{audience_id}/contacts/{contact_id}"
params = {k: v for k, v in {
"merge_field_validation_mode": merge_field_validation_mode,
"data_mode": data_mode,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
body = _parse_json_option(data, "--data") if data else None
client = get_client()
try:
result = client.patch(path, json=body, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@@ -0,0 +1,81 @@
"""Commands for the Mailchimp /authorized-apps API resource.
Auto-generated by cli_anything/mailchimp/_codegen/generate.py.
Do not edit manually — re-run the generator to update.
"""
import json
import click
def _parse_json_option(value, option_name, require_object=False):
try:
parsed = json.loads(value)
except json.JSONDecodeError as exc:
raise click.BadParameter(
f"must be valid JSON ({exc.msg})",
param_hint=option_name,
) from exc
if require_object and not isinstance(parsed, dict):
raise click.BadParameter("must be a JSON object", param_hint=option_name)
return parsed
@click.group("authorized-apps")
@click.pass_context
def authorized_apps_group(ctx):
"""authorized-apps resource commands."""
@authorized_apps_group.command("list")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--count", "count", default=None, type=int, help="The number of records to return. Default value is 10. Maximum value is 1000")
@click.option("--offset", "offset", default=None, type=int, help="Used for [pagination](https://mailchimp.com/developer/marketing/docs/methods-parameters/#pagination), this is the number of records from a collection to skip. Default value is 0.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_list_(ctx, fields, exclude_fields, count, offset, extra_params):
"""List authorized apps"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/authorized-apps"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
"count": count,
"offset": offset,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@authorized_apps_group.command("get")
@click.argument("APP_ID")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_get(ctx, app_id, fields, exclude_fields, extra_params):
"""Get authorized app info"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/authorized-apps/{app_id}"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@@ -0,0 +1,430 @@
"""Commands for the Mailchimp /automations API resource.
Auto-generated by cli_anything/mailchimp/_codegen/generate.py.
Do not edit manually — re-run the generator to update.
"""
import json
import click
def _parse_json_option(value, option_name, require_object=False):
try:
parsed = json.loads(value)
except json.JSONDecodeError as exc:
raise click.BadParameter(
f"must be valid JSON ({exc.msg})",
param_hint=option_name,
) from exc
if require_object and not isinstance(parsed, dict):
raise click.BadParameter("must be a JSON object", param_hint=option_name)
return parsed
@click.group("automations")
@click.pass_context
def automations_group(ctx):
"""automations resource commands."""
@automations_group.command("list")
@click.option("--count", "count", default=None, type=int, help="The number of records to return. Default value is 10. Maximum value is 1000")
@click.option("--offset", "offset", default=None, type=int, help="Used for [pagination](https://mailchimp.com/developer/marketing/docs/methods-parameters/#pagination), this is the number of records from a collection to skip. Default value is 0.")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--before-create-time", "before_create_time", default=None, help="Restrict the response to automations created before this time. Uses the ISO 8601 time format: 2015-10-21T15:41:36+00:00.")
@click.option("--since-create-time", "since_create_time", default=None, help="Restrict the response to automations created after this time. Uses the ISO 8601 time format: 2015-10-21T15:41:36+00:00.")
@click.option("--before-start-time", "before_start_time", default=None, help="Restrict the response to automations started before this time. Uses the ISO 8601 time format: 2015-10-21T15:41:36+00:00.")
@click.option("--since-start-time", "since_start_time", default=None, help="Restrict the response to automations started after this time. Uses the ISO 8601 time format: 2015-10-21T15:41:36+00:00.")
@click.option("--status", "status", default=None, help="Restrict the results to automations with the specified status.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_list_(ctx, count, offset, fields, exclude_fields, before_create_time, since_create_time, before_start_time, since_start_time, status, extra_params):
"""List automations"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/automations"
params = {k: v for k, v in {
"count": count,
"offset": offset,
"fields": fields,
"exclude_fields": exclude_fields,
"before_create_time": before_create_time,
"since_create_time": since_create_time,
"before_start_time": before_start_time,
"since_start_time": since_start_time,
"status": status,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@automations_group.command("create")
@click.option("--data", default=None, help="Request body as JSON string.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_create(ctx, data, extra_params):
"""Add automation"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/automations"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
body = _parse_json_option(data, "--data") if data else None
client = get_client()
try:
result = client.post(path, json=body, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@automations_group.command("get")
@click.argument("WORKFLOW_ID")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_get(ctx, workflow_id, fields, exclude_fields, extra_params):
"""Get automation info"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/automations/{workflow_id}"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@automations_group.command("archive")
@click.argument("WORKFLOW_ID")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_archive(ctx, workflow_id, extra_params):
"""Archive automation"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/automations/{workflow_id}/actions/archive"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.post(path, json=None, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@automations_group.command("pause-all-emails")
@click.argument("WORKFLOW_ID")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_pause_all_emails(ctx, workflow_id, extra_params):
"""Pause automation emails"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/automations/{workflow_id}/actions/pause-all-emails"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.post(path, json=None, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@automations_group.command("start-all-emails")
@click.argument("WORKFLOW_ID")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_start_all_emails(ctx, workflow_id, extra_params):
"""Start automation emails"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/automations/{workflow_id}/actions/start-all-emails"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.post(path, json=None, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@automations_group.command("list-automations-id-emails")
@click.argument("WORKFLOW_ID")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_list_automations_id_emails(ctx, workflow_id, extra_params):
"""List automated emails"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/automations/{workflow_id}/emails"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
automations_group.add_command(_cmd_list_automations_id_emails, "list-emails")
@automations_group.command("delete")
@click.argument("WORKFLOW_ID")
@click.argument("WORKFLOW_EMAIL_ID")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_delete(ctx, workflow_id, workflow_email_id, extra_params):
"""Delete workflow email"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/automations/{workflow_id}/emails/{workflow_email_id}"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.delete(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out_ok("Deleted.")
@automations_group.command("get-automations-id-emails-id")
@click.argument("WORKFLOW_ID")
@click.argument("WORKFLOW_EMAIL_ID")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_get_automations_id_emails_id(ctx, workflow_id, workflow_email_id, extra_params):
"""Get workflow email info"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/automations/{workflow_id}/emails/{workflow_email_id}"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@automations_group.command("update")
@click.argument("WORKFLOW_ID")
@click.argument("WORKFLOW_EMAIL_ID")
@click.option("--data", default=None, help="Request body as JSON string.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_update(ctx, workflow_id, workflow_email_id, data, extra_params):
"""Update workflow email"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/automations/{workflow_id}/emails/{workflow_email_id}"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
body = _parse_json_option(data, "--data") if data else None
client = get_client()
try:
result = client.patch(path, json=body, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@automations_group.command("pause")
@click.argument("WORKFLOW_ID")
@click.argument("WORKFLOW_EMAIL_ID")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_pause(ctx, workflow_id, workflow_email_id, extra_params):
"""Pause automated email"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/automations/{workflow_id}/emails/{workflow_email_id}/actions/pause"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.post(path, json=None, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@automations_group.command("start")
@click.argument("WORKFLOW_ID")
@click.argument("WORKFLOW_EMAIL_ID")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_start(ctx, workflow_id, workflow_email_id, extra_params):
"""Start automated email"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/automations/{workflow_id}/emails/{workflow_email_id}/actions/start"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.post(path, json=None, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@automations_group.command("list-automations-id-emails-id-queue")
@click.argument("WORKFLOW_ID")
@click.argument("WORKFLOW_EMAIL_ID")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_list_automations_id_emails_id_queue(ctx, workflow_id, workflow_email_id, extra_params):
"""List automated email subscribers"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/automations/{workflow_id}/emails/{workflow_email_id}/queue"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@automations_group.command("create-automations-id-emails-id-queue")
@click.argument("WORKFLOW_ID")
@click.argument("WORKFLOW_EMAIL_ID")
@click.option("--data", default=None, help="Request body as JSON string.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_create_automations_id_emails_id_queue(ctx, workflow_id, workflow_email_id, data, extra_params):
"""Add subscriber to workflow email"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/automations/{workflow_id}/emails/{workflow_email_id}/queue"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
body = _parse_json_option(data, "--data") if data else None
client = get_client()
try:
result = client.post(path, json=body, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@automations_group.command("get-automations-id-emails-id-queue-id")
@click.argument("WORKFLOW_ID")
@click.argument("WORKFLOW_EMAIL_ID")
@click.argument("SUBSCRIBER_HASH")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_get_automations_id_emails_id_queue_id(ctx, workflow_id, workflow_email_id, subscriber_hash, extra_params):
"""Get automated email subscriber"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/automations/{workflow_id}/emails/{workflow_email_id}/queue/{subscriber_hash}"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@automations_group.command("list-automations-id-removed-subscribers")
@click.argument("WORKFLOW_ID")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_list_automations_id_removed_subscribers(ctx, workflow_id, extra_params):
"""List subscribers removed from workflow"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/automations/{workflow_id}/removed-subscribers"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@automations_group.command("create-automations-id-removed-subscribers")
@click.argument("WORKFLOW_ID")
@click.option("--data", default=None, help="Request body as JSON string.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_create_automations_id_removed_subscribers(ctx, workflow_id, data, extra_params):
"""Remove subscriber from workflow"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/automations/{workflow_id}/removed-subscribers"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
body = _parse_json_option(data, "--data") if data else None
client = get_client()
try:
result = client.post(path, json=body, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@automations_group.command("get-automations-id-removed-subscribers-id")
@click.argument("WORKFLOW_ID")
@click.argument("SUBSCRIBER_HASH")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_get_automations_id_removed_subscribers_id(ctx, workflow_id, subscriber_hash, extra_params):
"""Get subscriber removed from workflow"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/automations/{workflow_id}/removed-subscribers/{subscriber_hash}"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@@ -0,0 +1,144 @@
"""Commands for the Mailchimp /batch-webhooks API resource.
Auto-generated by cli_anything/mailchimp/_codegen/generate.py.
Do not edit manually — re-run the generator to update.
"""
import json
import click
def _parse_json_option(value, option_name, require_object=False):
try:
parsed = json.loads(value)
except json.JSONDecodeError as exc:
raise click.BadParameter(
f"must be valid JSON ({exc.msg})",
param_hint=option_name,
) from exc
if require_object and not isinstance(parsed, dict):
raise click.BadParameter("must be a JSON object", param_hint=option_name)
return parsed
@click.group("batch-webhooks")
@click.pass_context
def batch_webhooks_group(ctx):
"""batch-webhooks resource commands."""
@batch_webhooks_group.command("list")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--count", "count", default=None, type=int, help="The number of records to return. Default value is 10. Maximum value is 1000")
@click.option("--offset", "offset", default=None, type=int, help="Used for [pagination](https://mailchimp.com/developer/marketing/docs/methods-parameters/#pagination), this is the number of records from a collection to skip. Default value is 0.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_list_(ctx, fields, exclude_fields, count, offset, extra_params):
"""List batch webhooks"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/batch-webhooks"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
"count": count,
"offset": offset,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@batch_webhooks_group.command("create")
@click.option("--data", default=None, help="Request body as JSON string.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_create(ctx, data, extra_params):
"""Add batch webhook"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/batch-webhooks"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
body = _parse_json_option(data, "--data") if data else None
client = get_client()
try:
result = client.post(path, json=body, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@batch_webhooks_group.command("delete")
@click.argument("BATCH_WEBHOOK_ID")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_delete(ctx, batch_webhook_id, extra_params):
"""Delete batch webhook"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/batch-webhooks/{batch_webhook_id}"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.delete(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out_ok("Deleted.")
@batch_webhooks_group.command("get")
@click.argument("BATCH_WEBHOOK_ID")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_get(ctx, batch_webhook_id, fields, exclude_fields, extra_params):
"""Get batch webhook info"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/batch-webhooks/{batch_webhook_id}"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@batch_webhooks_group.command("update")
@click.argument("BATCH_WEBHOOK_ID")
@click.option("--data", default=None, help="Request body as JSON string.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_update(ctx, batch_webhook_id, data, extra_params):
"""Update batch webhook"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/batch-webhooks/{batch_webhook_id}"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
body = _parse_json_option(data, "--data") if data else None
client = get_client()
try:
result = client.patch(path, json=body, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@@ -0,0 +1,122 @@
"""Commands for the Mailchimp /batches API resource.
Auto-generated by cli_anything/mailchimp/_codegen/generate.py.
Do not edit manually — re-run the generator to update.
"""
import json
import click
def _parse_json_option(value, option_name, require_object=False):
try:
parsed = json.loads(value)
except json.JSONDecodeError as exc:
raise click.BadParameter(
f"must be valid JSON ({exc.msg})",
param_hint=option_name,
) from exc
if require_object and not isinstance(parsed, dict):
raise click.BadParameter("must be a JSON object", param_hint=option_name)
return parsed
@click.group("batches")
@click.pass_context
def batches_group(ctx):
"""batches resource commands."""
@batches_group.command("list")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--count", "count", default=None, type=int, help="The number of records to return. Default value is 10. Maximum value is 1000")
@click.option("--offset", "offset", default=None, type=int, help="Used for [pagination](https://mailchimp.com/developer/marketing/docs/methods-parameters/#pagination), this is the number of records from a collection to skip. Default value is 0.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_list_(ctx, fields, exclude_fields, count, offset, extra_params):
"""List batch requests"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/batches"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
"count": count,
"offset": offset,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@batches_group.command("create")
@click.option("--data", default=None, help="Request body as JSON string.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_create(ctx, data, extra_params):
"""Start batch operation"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/batches"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
body = _parse_json_option(data, "--data") if data else None
client = get_client()
try:
result = client.post(path, json=body, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@batches_group.command("delete")
@click.argument("BATCH_ID")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_delete(ctx, batch_id, extra_params):
"""Delete batch request"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/batches/{batch_id}"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.delete(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out_ok("Deleted.")
@batches_group.command("get")
@click.argument("BATCH_ID")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_get(ctx, batch_id, fields, exclude_fields, extra_params):
"""Get batch operation status"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/batches/{batch_id}"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@@ -0,0 +1,144 @@
"""Commands for the Mailchimp /campaign-folders API resource.
Auto-generated by cli_anything/mailchimp/_codegen/generate.py.
Do not edit manually — re-run the generator to update.
"""
import json
import click
def _parse_json_option(value, option_name, require_object=False):
try:
parsed = json.loads(value)
except json.JSONDecodeError as exc:
raise click.BadParameter(
f"must be valid JSON ({exc.msg})",
param_hint=option_name,
) from exc
if require_object and not isinstance(parsed, dict):
raise click.BadParameter("must be a JSON object", param_hint=option_name)
return parsed
@click.group("campaign-folders")
@click.pass_context
def campaign_folders_group(ctx):
"""campaign-folders resource commands."""
@campaign_folders_group.command("list")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--count", "count", default=None, type=int, help="The number of records to return. Default value is 10. Maximum value is 1000")
@click.option("--offset", "offset", default=None, type=int, help="Used for [pagination](https://mailchimp.com/developer/marketing/docs/methods-parameters/#pagination), this is the number of records from a collection to skip. Default value is 0.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_list_(ctx, fields, exclude_fields, count, offset, extra_params):
"""List campaign folders"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/campaign-folders"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
"count": count,
"offset": offset,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@campaign_folders_group.command("create")
@click.option("--data", default=None, help="Request body as JSON string.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_create(ctx, data, extra_params):
"""Add campaign folder"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/campaign-folders"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
body = _parse_json_option(data, "--data") if data else None
client = get_client()
try:
result = client.post(path, json=body, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@campaign_folders_group.command("delete")
@click.argument("FOLDER_ID")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_delete(ctx, folder_id, extra_params):
"""Delete campaign folder"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/campaign-folders/{folder_id}"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.delete(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out_ok("Deleted.")
@campaign_folders_group.command("get")
@click.argument("FOLDER_ID")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_get(ctx, folder_id, fields, exclude_fields, extra_params):
"""Get campaign folder"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/campaign-folders/{folder_id}"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@campaign_folders_group.command("update")
@click.argument("FOLDER_ID")
@click.option("--data", default=None, help="Request body as JSON string.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_update(ctx, folder_id, data, extra_params):
"""Update campaign folder"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/campaign-folders/{folder_id}"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
body = _parse_json_option(data, "--data") if data else None
client = get_client()
try:
result = client.patch(path, json=body, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@@ -0,0 +1,553 @@
"""Commands for the Mailchimp /campaigns API resource.
Auto-generated by cli_anything/mailchimp/_codegen/generate.py.
Do not edit manually — re-run the generator to update.
"""
import json
import click
def _parse_json_option(value, option_name, require_object=False):
try:
parsed = json.loads(value)
except json.JSONDecodeError as exc:
raise click.BadParameter(
f"must be valid JSON ({exc.msg})",
param_hint=option_name,
) from exc
if require_object and not isinstance(parsed, dict):
raise click.BadParameter("must be a JSON object", param_hint=option_name)
return parsed
@click.group("campaigns")
@click.pass_context
def campaigns_group(ctx):
"""campaigns resource commands."""
@campaigns_group.command("list")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--count", "count", default=None, type=int, help="The number of records to return. Default value is 10. Maximum value is 1000")
@click.option("--offset", "offset", default=None, type=int, help="Used for [pagination](https://mailchimp.com/developer/marketing/docs/methods-parameters/#pagination), this is the number of records from a collection to skip. Default value is 0.")
@click.option("--type", "type_", default=None, help="The campaign type.")
@click.option("--status", "status", default=None, help="The status of the campaign.")
@click.option("--before-send-time", "before_send_time", default=None, help="Restrict the response to campaigns sent before the set time. Uses ISO 8601 time format: 2015-10-21T15:41:36+00:00.")
@click.option("--since-send-time", "since_send_time", default=None, help="Restrict the response to campaigns sent after the set time. Uses ISO 8601 time format: 2015-10-21T15:41:36+00:00.")
@click.option("--before-create-time", "before_create_time", default=None, help="Restrict the response to campaigns created before the set time. Uses ISO 8601 time format: 2015-10-21T15:41:36+00:00.")
@click.option("--since-create-time", "since_create_time", default=None, help="Restrict the response to campaigns created after the set time. Uses ISO 8601 time format: 2015-10-21T15:41:36+00:00.")
@click.option("--list-id", "list_id", default=None, help="The unique id for the list.")
@click.option("--folder-id", "folder_id", default=None, help="The unique folder id.")
@click.option("--member-id", "member_id", default=None, help="Retrieve campaigns sent to a particular list member. Member ID is The MD5 hash of the lowercase version of the list member’s email address.")
@click.option("--sort-field", "sort_field", default=None, help="Returns files sorted by the specified field.")
@click.option("--sort-dir", "sort_dir", default=None, help="Determines the order direction for sorted results.")
@click.option("--include-resend-shortcut-eligibility", "include_resend_shortcut_eligibility", default=None, type=bool, help="Return the `resend_shortcut_eligibility` field in the response, which tells you if the campaign is eligible for the various Campaign Resend Shortcuts offered.")
@click.option("--include-resend-shortcut-usage", "include_resend_shortcut_usage", default=None, type=bool, help="Return the `resend_shortcut_usage` field in the response. This includes information about campaigns related by a shortcut.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_list_(ctx, fields, exclude_fields, count, offset, type_, status, before_send_time, since_send_time, before_create_time, since_create_time, list_id, folder_id, member_id, sort_field, sort_dir, include_resend_shortcut_eligibility, include_resend_shortcut_usage, extra_params):
"""List campaigns"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/campaigns"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
"count": count,
"offset": offset,
"type": type_,
"status": status,
"before_send_time": before_send_time,
"since_send_time": since_send_time,
"before_create_time": before_create_time,
"since_create_time": since_create_time,
"list_id": list_id,
"folder_id": folder_id,
"member_id": member_id,
"sort_field": sort_field,
"sort_dir": sort_dir,
"include_resend_shortcut_eligibility": include_resend_shortcut_eligibility,
"include_resend_shortcut_usage": include_resend_shortcut_usage,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@campaigns_group.command("create")
@click.option("--data", default=None, help="Request body as JSON string.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_create(ctx, data, extra_params):
"""Add campaign"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/campaigns"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
body = _parse_json_option(data, "--data") if data else None
client = get_client()
try:
result = client.post(path, json=body, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@campaigns_group.command("delete")
@click.argument("CAMPAIGN_ID")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_delete(ctx, campaign_id, extra_params):
"""Delete campaign"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/campaigns/{campaign_id}"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.delete(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out_ok("Deleted.")
@campaigns_group.command("get")
@click.argument("CAMPAIGN_ID")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--include-resend-shortcut-eligibility", "include_resend_shortcut_eligibility", default=None, type=bool, help="Return the `resend_shortcut_eligibility` field in the response, which tells you if the campaign is eligible for the various Campaign Resend Shortcuts offered.")
@click.option("--include-resend-shortcut-usage", "include_resend_shortcut_usage", default=None, type=bool, help="Return the `resend_shortcut_usage` field in the response. This includes information about campaigns related by a shortcut.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_get(ctx, campaign_id, fields, exclude_fields, include_resend_shortcut_eligibility, include_resend_shortcut_usage, extra_params):
"""Get campaign info"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/campaigns/{campaign_id}"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
"include_resend_shortcut_eligibility": include_resend_shortcut_eligibility,
"include_resend_shortcut_usage": include_resend_shortcut_usage,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@campaigns_group.command("update")
@click.argument("CAMPAIGN_ID")
@click.option("--data", default=None, help="Request body as JSON string.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_update(ctx, campaign_id, data, extra_params):
"""Update campaign settings"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/campaigns/{campaign_id}"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
body = _parse_json_option(data, "--data") if data else None
client = get_client()
try:
result = client.patch(path, json=body, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@campaigns_group.command("cancel-send")
@click.argument("CAMPAIGN_ID")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_cancel_send(ctx, campaign_id, extra_params):
"""Cancel campaign"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/campaigns/{campaign_id}/actions/cancel-send"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.post(path, json=None, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@campaigns_group.command("create-resend")
@click.argument("CAMPAIGN_ID")
@click.option("--data", default=None, help="Request body as JSON string.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_create_resend(ctx, campaign_id, data, extra_params):
"""Resend campaign"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/campaigns/{campaign_id}/actions/create-resend"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
body = _parse_json_option(data, "--data") if data else None
client = get_client()
try:
result = client.post(path, json=body, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@campaigns_group.command("pause")
@click.argument("CAMPAIGN_ID")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_pause(ctx, campaign_id, extra_params):
"""Pause rss campaign"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/campaigns/{campaign_id}/actions/pause"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.post(path, json=None, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@campaigns_group.command("replicate")
@click.argument("CAMPAIGN_ID")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_replicate(ctx, campaign_id, extra_params):
"""Replicate campaign"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/campaigns/{campaign_id}/actions/replicate"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.post(path, json=None, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@campaigns_group.command("resume")
@click.argument("CAMPAIGN_ID")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_resume(ctx, campaign_id, extra_params):
"""Resume rss campaign"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/campaigns/{campaign_id}/actions/resume"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.post(path, json=None, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@campaigns_group.command("schedule")
@click.argument("CAMPAIGN_ID")
@click.option("--data", default=None, help="Request body as JSON string.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_schedule(ctx, campaign_id, data, extra_params):
"""Schedule campaign"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/campaigns/{campaign_id}/actions/schedule"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
body = _parse_json_option(data, "--data") if data else None
client = get_client()
try:
result = client.post(path, json=body, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@campaigns_group.command("send")
@click.argument("CAMPAIGN_ID")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_send(ctx, campaign_id, extra_params):
"""Send campaign"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/campaigns/{campaign_id}/actions/send"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.post(path, json=None, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@campaigns_group.command("test")
@click.argument("CAMPAIGN_ID")
@click.option("--data", default=None, help="Request body as JSON string.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_test(ctx, campaign_id, data, extra_params):
"""Send test email"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/campaigns/{campaign_id}/actions/test"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
body = _parse_json_option(data, "--data") if data else None
client = get_client()
try:
result = client.post(path, json=body, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@campaigns_group.command("unschedule")
@click.argument("CAMPAIGN_ID")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_unschedule(ctx, campaign_id, extra_params):
"""Unschedule campaign"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/campaigns/{campaign_id}/actions/unschedule"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.post(path, json=None, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@campaigns_group.command("list-campaigns-id-content")
@click.argument("CAMPAIGN_ID")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_list_campaigns_id_content(ctx, campaign_id, fields, exclude_fields, extra_params):
"""Get campaign content"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/campaigns/{campaign_id}/content"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
campaigns_group.add_command(_cmd_list_campaigns_id_content, "list-content")
@campaigns_group.command("update-campaigns-id-content")
@click.argument("CAMPAIGN_ID")
@click.option("--data", default=None, help="Request body as JSON string.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_update_campaigns_id_content(ctx, campaign_id, data, extra_params):
"""Set campaign content"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/campaigns/{campaign_id}/content"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
body = _parse_json_option(data, "--data") if data else None
client = get_client()
try:
result = client.put(path, json=body, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@campaigns_group.command("list-campaigns-id-feedback")
@click.argument("CAMPAIGN_ID")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_list_campaigns_id_feedback(ctx, campaign_id, fields, exclude_fields, extra_params):
"""List campaign feedback"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/campaigns/{campaign_id}/feedback"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@campaigns_group.command("create-campaigns-id-feedback")
@click.argument("CAMPAIGN_ID")
@click.option("--data", default=None, help="Request body as JSON string.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_create_campaigns_id_feedback(ctx, campaign_id, data, extra_params):
"""Add campaign feedback"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/campaigns/{campaign_id}/feedback"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
body = _parse_json_option(data, "--data") if data else None
client = get_client()
try:
result = client.post(path, json=body, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@campaigns_group.command("delete-campaigns-id-feedback-id")
@click.argument("CAMPAIGN_ID")
@click.argument("FEEDBACK_ID")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_delete_campaigns_id_feedback_id(ctx, campaign_id, feedback_id, extra_params):
"""Delete campaign feedback message"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/campaigns/{campaign_id}/feedback/{feedback_id}"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.delete(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out_ok("Deleted.")
@campaigns_group.command("get-campaigns-id-feedback-id")
@click.argument("CAMPAIGN_ID")
@click.argument("FEEDBACK_ID")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_get_campaigns_id_feedback_id(ctx, campaign_id, feedback_id, fields, exclude_fields, extra_params):
"""Get campaign feedback message"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/campaigns/{campaign_id}/feedback/{feedback_id}"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@campaigns_group.command("update-campaigns-id-feedback-id")
@click.argument("CAMPAIGN_ID")
@click.argument("FEEDBACK_ID")
@click.option("--data", default=None, help="Request body as JSON string.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_update_campaigns_id_feedback_id(ctx, campaign_id, feedback_id, data, extra_params):
"""Update campaign feedback message"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/campaigns/{campaign_id}/feedback/{feedback_id}"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
body = _parse_json_option(data, "--data") if data else None
client = get_client()
try:
result = client.patch(path, json=body, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@campaigns_group.command("list-campaigns-id-send-checklist")
@click.argument("CAMPAIGN_ID")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_list_campaigns_id_send_checklist(ctx, campaign_id, fields, exclude_fields, extra_params):
"""Get campaign send checklist"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/campaigns/{campaign_id}/send-checklist"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
campaigns_group.add_command(_cmd_list_campaigns_id_send_checklist, "list-send-checklist")
@@ -0,0 +1,142 @@
"""Commands for the Mailchimp /connected-sites API resource.
Auto-generated by cli_anything/mailchimp/_codegen/generate.py.
Do not edit manually — re-run the generator to update.
"""
import json
import click
def _parse_json_option(value, option_name, require_object=False):
try:
parsed = json.loads(value)
except json.JSONDecodeError as exc:
raise click.BadParameter(
f"must be valid JSON ({exc.msg})",
param_hint=option_name,
) from exc
if require_object and not isinstance(parsed, dict):
raise click.BadParameter("must be a JSON object", param_hint=option_name)
return parsed
@click.group("connected-sites")
@click.pass_context
def connected_sites_group(ctx):
"""connected-sites resource commands."""
@connected_sites_group.command("list")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--count", "count", default=None, type=int, help="The number of records to return. Default value is 10. Maximum value is 1000")
@click.option("--offset", "offset", default=None, type=int, help="Used for [pagination](https://mailchimp.com/developer/marketing/docs/methods-parameters/#pagination), this is the number of records from a collection to skip. Default value is 0.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_list_(ctx, fields, exclude_fields, count, offset, extra_params):
"""List connected sites"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/connected-sites"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
"count": count,
"offset": offset,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@connected_sites_group.command("create")
@click.option("--data", default=None, help="Request body as JSON string.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_create(ctx, data, extra_params):
"""Add connected site"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/connected-sites"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
body = _parse_json_option(data, "--data") if data else None
client = get_client()
try:
result = client.post(path, json=body, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@connected_sites_group.command("delete")
@click.argument("CONNECTED_SITE_ID")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_delete(ctx, connected_site_id, extra_params):
"""Delete connected site"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/connected-sites/{connected_site_id}"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.delete(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out_ok("Deleted.")
@connected_sites_group.command("get")
@click.argument("CONNECTED_SITE_ID")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_get(ctx, connected_site_id, fields, exclude_fields, extra_params):
"""Get connected site"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/connected-sites/{connected_site_id}"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@connected_sites_group.command("verify-script-installation")
@click.argument("CONNECTED_SITE_ID")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_verify_script_installation(ctx, connected_site_id, extra_params):
"""Verify connected site script"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/connected-sites/{connected_site_id}/actions/verify-script-installation"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.post(path, json=None, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@@ -0,0 +1,123 @@
"""Commands for the Mailchimp /contacts API resource.
Auto-generated by cli_anything/mailchimp/_codegen/generate.py.
Do not edit manually — re-run the generator to update.
"""
import json
import click
def _parse_json_option(value, option_name, require_object=False):
try:
parsed = json.loads(value)
except json.JSONDecodeError as exc:
raise click.BadParameter(
f"must be valid JSON ({exc.msg})",
param_hint=option_name,
) from exc
if require_object and not isinstance(parsed, dict):
raise click.BadParameter("must be a JSON object", param_hint=option_name)
return parsed
@click.group("contacts")
@click.pass_context
def contacts_group(ctx):
"""contacts resource commands."""
@contacts_group.command("list")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--count", "count", default=None, type=int, help="The number of records to return. Default value is 10. Maximum value is 1000")
@click.option("--offset", "offset", default=None, type=int, help="Used for [pagination](https://mailchimp.com/developer/marketing/docs/methods-parameters/#pagination), this is the number of records from a collection to skip. Default value is 0.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_list_(ctx, fields, exclude_fields, count, offset, extra_params):
"""Get a list of audiences"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/audiences"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
"count": count,
"offset": offset,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@contacts_group.command("get")
@click.argument("AUDIENCE_ID")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_get(ctx, audience_id, fields, exclude_fields, extra_params):
"""Get audience info"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/audiences/{audience_id}"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@contacts_group.command("archive")
@click.argument("AUDIENCE_ID")
@click.argument("CONTACT_ID")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_archive(ctx, audience_id, contact_id, extra_params):
"""Archive Contact"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/audiences/{audience_id}/contacts/{contact_id}/actions/archive"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.post(path, json=None, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@contacts_group.command("forget")
@click.argument("AUDIENCE_ID")
@click.argument("CONTACT_ID")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_forget(ctx, audience_id, contact_id, extra_params):
"""Forget Contact"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/audiences/{audience_id}/contacts/{contact_id}/actions/forget"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.post(path, json=None, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@@ -0,0 +1,144 @@
"""Commands for the Mailchimp /conversations API resource.
Auto-generated by cli_anything/mailchimp/_codegen/generate.py.
Do not edit manually — re-run the generator to update.
"""
import json
import click
def _parse_json_option(value, option_name, require_object=False):
try:
parsed = json.loads(value)
except json.JSONDecodeError as exc:
raise click.BadParameter(
f"must be valid JSON ({exc.msg})",
param_hint=option_name,
) from exc
if require_object and not isinstance(parsed, dict):
raise click.BadParameter("must be a JSON object", param_hint=option_name)
return parsed
@click.group("conversations")
@click.pass_context
def conversations_group(ctx):
"""conversations resource commands."""
@conversations_group.command("list")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--count", "count", default=None, type=int, help="The number of records to return. Default value is 10. Maximum value is 1000")
@click.option("--offset", "offset", default=None, type=int, help="Used for [pagination](https://mailchimp.com/developer/marketing/docs/methods-parameters/#pagination), this is the number of records from a collection to skip. Default value is 0.")
@click.option("--has-unread-messages", "has_unread_messages", default=None, help="Whether the conversation has any unread messages.")
@click.option("--list-id", "list_id", default=None, help="The unique id for the list.")
@click.option("--campaign-id", "campaign_id", default=None, help="The unique id for the campaign.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_list_(ctx, fields, exclude_fields, count, offset, has_unread_messages, list_id, campaign_id, extra_params):
"""List conversations"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/conversations"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
"count": count,
"offset": offset,
"has_unread_messages": has_unread_messages,
"list_id": list_id,
"campaign_id": campaign_id,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@conversations_group.command("get")
@click.argument("CONVERSATION_ID")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_get(ctx, conversation_id, fields, exclude_fields, extra_params):
"""Get conversation"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/conversations/{conversation_id}"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@conversations_group.command("list-conversations-id-messages")
@click.argument("CONVERSATION_ID")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--is-read", "is_read", default=None, help="Whether a conversation message has been marked as read.")
@click.option("--before-timestamp", "before_timestamp", default=None, help="Restrict the response to messages created before the set time. Uses ISO 8601 time format: 2015-10-21T15:41:36+00:00.")
@click.option("--since-timestamp", "since_timestamp", default=None, help="Restrict the response to messages created after the set time. Uses ISO 8601 time format: 2015-10-21T15:41:36+00:00.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_list_conversations_id_messages(ctx, conversation_id, fields, exclude_fields, is_read, before_timestamp, since_timestamp, extra_params):
"""List messages"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/conversations/{conversation_id}/messages"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
"is_read": is_read,
"before_timestamp": before_timestamp,
"since_timestamp": since_timestamp,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@conversations_group.command("get-conversations-id-messages-id")
@click.argument("CONVERSATION_ID")
@click.argument("MESSAGE_ID")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_get_conversations_id_messages_id(ctx, conversation_id, message_id, fields, exclude_fields, extra_params):
"""Get message"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/conversations/{conversation_id}/messages/{message_id}"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@@ -0,0 +1,51 @@
"""Commands for the Mailchimp /customer-journeys API resource.
Auto-generated by cli_anything/mailchimp/_codegen/generate.py.
Do not edit manually — re-run the generator to update.
"""
import json
import click
def _parse_json_option(value, option_name, require_object=False):
try:
parsed = json.loads(value)
except json.JSONDecodeError as exc:
raise click.BadParameter(
f"must be valid JSON ({exc.msg})",
param_hint=option_name,
) from exc
if require_object and not isinstance(parsed, dict):
raise click.BadParameter("must be a JSON object", param_hint=option_name)
return parsed
@click.group("customer-journeys")
@click.pass_context
def customer_journeys_group(ctx):
"""customer-journeys resource commands."""
@customer_journeys_group.command("trigger")
@click.argument("JOURNEY_ID")
@click.argument("STEP_ID")
@click.option("--data", default=None, help="Request body as JSON string.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_trigger(ctx, journey_id, step_id, data, extra_params):
"""Customer Journeys API trigger for a contact"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/customer-journeys/journeys/{journey_id}/steps/{step_id}/actions/trigger"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
body = _parse_json_option(data, "--data") if data else None
client = get_client()
try:
result = client.post(path, json=body, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,85 @@
"""Commands for the Mailchimp /facebook-ads API resource.
Auto-generated by cli_anything/mailchimp/_codegen/generate.py.
Do not edit manually — re-run the generator to update.
"""
import json
import click
def _parse_json_option(value, option_name, require_object=False):
try:
parsed = json.loads(value)
except json.JSONDecodeError as exc:
raise click.BadParameter(
f"must be valid JSON ({exc.msg})",
param_hint=option_name,
) from exc
if require_object and not isinstance(parsed, dict):
raise click.BadParameter("must be a JSON object", param_hint=option_name)
return parsed
@click.group("facebook-ads")
@click.pass_context
def facebook_ads_group(ctx):
"""facebook-ads resource commands."""
@facebook_ads_group.command("list")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--count", "count", default=None, type=int, help="The number of records to return. Default value is 10. Maximum value is 1000")
@click.option("--offset", "offset", default=None, type=int, help="Used for [pagination](https://mailchimp.com/developer/marketing/docs/methods-parameters/#pagination), this is the number of records from a collection to skip. Default value is 0.")
@click.option("--sort-field", "sort_field", default=None, help="Returns files sorted by the specified field.")
@click.option("--sort-dir", "sort_dir", default=None, help="Determines the order direction for sorted results.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_list_(ctx, fields, exclude_fields, count, offset, sort_field, sort_dir, extra_params):
"""List facebook ads"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/facebook-ads"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
"count": count,
"offset": offset,
"sort_field": sort_field,
"sort_dir": sort_dir,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@facebook_ads_group.command("get")
@click.argument("OUTREACH_ID")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_get(ctx, outreach_id, fields, exclude_fields, extra_params):
"""Get facebook ad info"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/facebook-ads/{outreach_id}"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@@ -0,0 +1,319 @@
"""Commands for the Mailchimp /file-manager API resource.
Auto-generated by cli_anything/mailchimp/_codegen/generate.py.
Do not edit manually — re-run the generator to update.
"""
import json
import click
def _parse_json_option(value, option_name, require_object=False):
try:
parsed = json.loads(value)
except json.JSONDecodeError as exc:
raise click.BadParameter(
f"must be valid JSON ({exc.msg})",
param_hint=option_name,
) from exc
if require_object and not isinstance(parsed, dict):
raise click.BadParameter("must be a JSON object", param_hint=option_name)
return parsed
@click.group("file-manager")
@click.pass_context
def file_manager_group(ctx):
"""file-manager resource commands."""
@file_manager_group.command("list")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--count", "count", default=None, type=int, help="The number of records to return. Default value is 10. Maximum value is 1000")
@click.option("--offset", "offset", default=None, type=int, help="Used for [pagination](https://mailchimp.com/developer/marketing/docs/methods-parameters/#pagination), this is the number of records from a collection to skip. Default value is 0.")
@click.option("--type", "type_", default=None, help="The file type for the File Manager file.")
@click.option("--created-by", "created_by", default=None, help="The Mailchimp account user who created the File Manager file.")
@click.option("--before-created-at", "before_created_at", default=None, help="Restrict the response to files created before the set date. Uses ISO 8601 time format: 2015-10-21T15:41:36+00:00.")
@click.option("--since-created-at", "since_created_at", default=None, help="Restrict the response to files created after the set date. Uses ISO 8601 time format: 2015-10-21T15:41:36+00:00.")
@click.option("--sort-field", "sort_field", default=None, help="Returns files sorted by the specified field.")
@click.option("--sort-dir", "sort_dir", default=None, help="Determines the order direction for sorted results.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_list_(ctx, fields, exclude_fields, count, offset, type_, created_by, before_created_at, since_created_at, sort_field, sort_dir, extra_params):
"""List stored files"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/file-manager/files"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
"count": count,
"offset": offset,
"type": type_,
"created_by": created_by,
"before_created_at": before_created_at,
"since_created_at": since_created_at,
"sort_field": sort_field,
"sort_dir": sort_dir,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@file_manager_group.command("create")
@click.option("--data", default=None, help="Request body as JSON string.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_create(ctx, data, extra_params):
"""Add file"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/file-manager/files"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
body = _parse_json_option(data, "--data") if data else None
client = get_client()
try:
result = client.post(path, json=body, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@file_manager_group.command("delete")
@click.argument("FILE_ID")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_delete(ctx, file_id, extra_params):
"""Delete file"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/file-manager/files/{file_id}"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.delete(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out_ok("Deleted.")
@file_manager_group.command("get")
@click.argument("FILE_ID")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_get(ctx, file_id, fields, exclude_fields, extra_params):
"""Get file"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/file-manager/files/{file_id}"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@file_manager_group.command("update")
@click.argument("FILE_ID")
@click.option("--data", default=None, help="Request body as JSON string.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_update(ctx, file_id, data, extra_params):
"""Update file"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/file-manager/files/{file_id}"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
body = _parse_json_option(data, "--data") if data else None
client = get_client()
try:
result = client.patch(path, json=body, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@file_manager_group.command("list-file-manager-folders")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--count", "count", default=None, type=int, help="The number of records to return. Default value is 10. Maximum value is 1000")
@click.option("--offset", "offset", default=None, type=int, help="Used for [pagination](https://mailchimp.com/developer/marketing/docs/methods-parameters/#pagination), this is the number of records from a collection to skip. Default value is 0.")
@click.option("--created-by", "created_by", default=None, help="The Mailchimp account user who created the File Manager file.")
@click.option("--before-created-at", "before_created_at", default=None, help="Restrict the response to files created before the set date. Uses ISO 8601 time format: 2015-10-21T15:41:36+00:00.")
@click.option("--since-created-at", "since_created_at", default=None, help="Restrict the response to files created after the set date. Uses ISO 8601 time format: 2015-10-21T15:41:36+00:00.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_list_file_manager_folders(ctx, fields, exclude_fields, count, offset, created_by, before_created_at, since_created_at, extra_params):
"""List folders"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/file-manager/folders"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
"count": count,
"offset": offset,
"created_by": created_by,
"before_created_at": before_created_at,
"since_created_at": since_created_at,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@file_manager_group.command("create-file-manager-folders")
@click.option("--data", default=None, help="Request body as JSON string.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_create_file_manager_folders(ctx, data, extra_params):
"""Add folder"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/file-manager/folders"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
body = _parse_json_option(data, "--data") if data else None
client = get_client()
try:
result = client.post(path, json=body, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@file_manager_group.command("delete-file-manager-folders-id")
@click.argument("FOLDER_ID")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_delete_file_manager_folders_id(ctx, folder_id, extra_params):
"""Delete folder"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/file-manager/folders/{folder_id}"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.delete(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out_ok("Deleted.")
@file_manager_group.command("get-file-manager-folders-id")
@click.argument("FOLDER_ID")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_get_file_manager_folders_id(ctx, folder_id, fields, exclude_fields, extra_params):
"""Get folder"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/file-manager/folders/{folder_id}"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@file_manager_group.command("update-file-manager-folders-id")
@click.argument("FOLDER_ID")
@click.option("--data", default=None, help="Request body as JSON string.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_update_file_manager_folders_id(ctx, folder_id, data, extra_params):
"""Update folder"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/file-manager/folders/{folder_id}"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
body = _parse_json_option(data, "--data") if data else None
client = get_client()
try:
result = client.patch(path, json=body, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@file_manager_group.command("list-file-manager-folders-files")
@click.argument("FOLDER_ID")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--count", "count", default=None, type=int, help="The number of records to return. Default value is 10. Maximum value is 1000")
@click.option("--offset", "offset", default=None, type=int, help="Used for [pagination](https://mailchimp.com/developer/marketing/docs/methods-parameters/#pagination), this is the number of records from a collection to skip. Default value is 0.")
@click.option("--type", "type_", default=None, help="The file type for the File Manager file.")
@click.option("--created-by", "created_by", default=None, help="The Mailchimp account user who created the File Manager file.")
@click.option("--before-created-at", "before_created_at", default=None, help="Restrict the response to files created before the set date. Uses ISO 8601 time format: 2015-10-21T15:41:36+00:00.")
@click.option("--since-created-at", "since_created_at", default=None, help="Restrict the response to files created after the set date. Uses ISO 8601 time format: 2015-10-21T15:41:36+00:00.")
@click.option("--sort-field", "sort_field", default=None, help="Returns files sorted by the specified field.")
@click.option("--sort-dir", "sort_dir", default=None, help="Determines the order direction for sorted results.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_list_file_manager_folders_files(ctx, folder_id, fields, exclude_fields, count, offset, type_, created_by, before_created_at, since_created_at, sort_field, sort_dir, extra_params):
"""List stored files"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/file-manager/folders/{folder_id}/files"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
"count": count,
"offset": offset,
"type": type_,
"created_by": created_by,
"before_created_at": before_created_at,
"since_created_at": since_created_at,
"sort_field": sort_field,
"sort_dir": sort_dir,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@@ -0,0 +1,214 @@
"""Commands for the Mailchimp /landing-pages API resource.
Auto-generated by cli_anything/mailchimp/_codegen/generate.py.
Do not edit manually — re-run the generator to update.
"""
import json
import click
def _parse_json_option(value, option_name, require_object=False):
try:
parsed = json.loads(value)
except json.JSONDecodeError as exc:
raise click.BadParameter(
f"must be valid JSON ({exc.msg})",
param_hint=option_name,
) from exc
if require_object and not isinstance(parsed, dict):
raise click.BadParameter("must be a JSON object", param_hint=option_name)
return parsed
@click.group("landing-pages")
@click.pass_context
def landing_pages_group(ctx):
"""landing-pages resource commands."""
@landing_pages_group.command("list")
@click.option("--sort-dir", "sort_dir", default=None, help="Determines the order direction for sorted results.")
@click.option("--sort-field", "sort_field", default=None, help="Returns files sorted by the specified field.")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--count", "count", default=None, type=int, help="The number of records to return. Default value is 10. Maximum value is 1000")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_list_(ctx, sort_dir, sort_field, fields, exclude_fields, count, extra_params):
"""List landing pages"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/landing-pages"
params = {k: v for k, v in {
"sort_dir": sort_dir,
"sort_field": sort_field,
"fields": fields,
"exclude_fields": exclude_fields,
"count": count,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@landing_pages_group.command("create")
@click.option("--data", default=None, help="Request body as JSON string.")
@click.option("--use-default-list", "use_default_list", default=None, type=bool, help="Will create the Landing Page using the account's Default List instead of requiring a list_id.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_create(ctx, data, use_default_list, extra_params):
"""Add landing page"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/landing-pages"
params = {k: v for k, v in {
"use_default_list": use_default_list,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
body = _parse_json_option(data, "--data") if data else None
client = get_client()
try:
result = client.post(path, json=body, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@landing_pages_group.command("delete")
@click.argument("PAGE_ID")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_delete(ctx, page_id, extra_params):
"""Delete landing page"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/landing-pages/{page_id}"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.delete(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out_ok("Deleted.")
@landing_pages_group.command("get")
@click.argument("PAGE_ID")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_get(ctx, page_id, fields, exclude_fields, extra_params):
"""Get landing page info"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/landing-pages/{page_id}"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@landing_pages_group.command("update")
@click.argument("PAGE_ID")
@click.option("--data", default=None, help="Request body as JSON string.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_update(ctx, page_id, data, extra_params):
"""Update landing page"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/landing-pages/{page_id}"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
body = _parse_json_option(data, "--data") if data else None
client = get_client()
try:
result = client.patch(path, json=body, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@landing_pages_group.command("publish")
@click.argument("PAGE_ID")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_publish(ctx, page_id, extra_params):
"""Publish landing page"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/landing-pages/{page_id}/actions/publish"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.post(path, json=None, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@landing_pages_group.command("unpublish")
@click.argument("PAGE_ID")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_unpublish(ctx, page_id, extra_params):
"""Unpublish landing page"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/landing-pages/{page_id}/actions/unpublish"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.post(path, json=None, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@landing_pages_group.command("list-landing-page-id-content")
@click.argument("PAGE_ID")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_list_landing_page_id_content(ctx, page_id, fields, exclude_fields, extra_params):
"""Get landing page content"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/landing-pages/{page_id}/content"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,49 @@
"""Commands for the Mailchimp /ping API resource.
Auto-generated by cli_anything/mailchimp/_codegen/generate.py.
Do not edit manually — re-run the generator to update.
"""
import json
import click
def _parse_json_option(value, option_name, require_object=False):
try:
parsed = json.loads(value)
except json.JSONDecodeError as exc:
raise click.BadParameter(
f"must be valid JSON ({exc.msg})",
param_hint=option_name,
) from exc
if require_object and not isinstance(parsed, dict):
raise click.BadParameter("must be a JSON object", param_hint=option_name)
return parsed
@click.group("ping", invoke_without_command=True)
@click.pass_context
def ping_group(ctx):
"""ping resource commands."""
if ctx.invoked_subcommand is None:
ctx.invoke(_cmd_list_)
@ping_group.command("list")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_list_(ctx, extra_params):
"""Ping"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/ping"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@@ -0,0 +1,353 @@
"""Commands for the Mailchimp /reporting API resource.
Auto-generated by cli_anything/mailchimp/_codegen/generate.py.
Do not edit manually — re-run the generator to update.
"""
import json
import click
def _parse_json_option(value, option_name, require_object=False):
try:
parsed = json.loads(value)
except json.JSONDecodeError as exc:
raise click.BadParameter(
f"must be valid JSON ({exc.msg})",
param_hint=option_name,
) from exc
if require_object and not isinstance(parsed, dict):
raise click.BadParameter("must be a JSON object", param_hint=option_name)
return parsed
@click.group("reporting")
@click.pass_context
def reporting_group(ctx):
"""reporting resource commands."""
@reporting_group.command("list")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--count", "count", default=None, type=int, help="The number of records to return. Default value is 10. Maximum value is 1000")
@click.option("--offset", "offset", default=None, type=int, help="Used for [pagination](https://mailchimp.com/developer/marketing/docs/methods-parameters/#pagination), this is the number of records from a collection to skip. Default value is 0.")
@click.option("--sort-field", "sort_field", default=None, help="Returns files sorted by the specified field.")
@click.option("--sort-dir", "sort_dir", default=None, help="Determines the order direction for sorted results.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_list_(ctx, fields, exclude_fields, count, offset, sort_field, sort_dir, extra_params):
"""List facebook ads reports"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/reporting/facebook-ads"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
"count": count,
"offset": offset,
"sort_field": sort_field,
"sort_dir": sort_dir,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@reporting_group.command("get")
@click.argument("OUTREACH_ID")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_get(ctx, outreach_id, fields, exclude_fields, extra_params):
"""Get facebook ad report"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/reporting/facebook-ads/{outreach_id}"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@reporting_group.command("list-reporting-facebook-ads-id-ecommerce-product-activity")
@click.argument("OUTREACH_ID")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--count", "count", default=None, type=int, help="The number of records to return. Default value is 10. Maximum value is 1000")
@click.option("--offset", "offset", default=None, type=int, help="Used for [pagination](https://mailchimp.com/developer/marketing/docs/methods-parameters/#pagination), this is the number of records from a collection to skip. Default value is 0.")
@click.option("--sort-field", "sort_field", default=None, help="Returns files sorted by the specified field.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_list_reporting_facebook_ads_id_ecommerce_product_activity(ctx, outreach_id, fields, exclude_fields, count, offset, sort_field, extra_params):
"""List facebook ecommerce report"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/reporting/facebook-ads/{outreach_id}/ecommerce-product-activity"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
"count": count,
"offset": offset,
"sort_field": sort_field,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@reporting_group.command("list-reporting-landing-pages")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--count", "count", default=None, type=int, help="The number of records to return. Default value is 10. Maximum value is 1000")
@click.option("--offset", "offset", default=None, type=int, help="Used for [pagination](https://mailchimp.com/developer/marketing/docs/methods-parameters/#pagination), this is the number of records from a collection to skip. Default value is 0.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_list_reporting_landing_pages(ctx, fields, exclude_fields, count, offset, extra_params):
"""List landing pages reports"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/reporting/landing-pages"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
"count": count,
"offset": offset,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@reporting_group.command("get-reporting-landing-pages-id")
@click.argument("OUTREACH_ID")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_get_reporting_landing_pages_id(ctx, outreach_id, fields, exclude_fields, extra_params):
"""Get landing page report"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/reporting/landing-pages/{outreach_id}"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@reporting_group.command("list-reporting-surveys")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--count", "count", default=None, type=int, help="The number of records to return. Default value is 10. Maximum value is 1000")
@click.option("--offset", "offset", default=None, type=int, help="Used for [pagination](https://mailchimp.com/developer/marketing/docs/methods-parameters/#pagination), this is the number of records from a collection to skip. Default value is 0.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_list_reporting_surveys(ctx, fields, exclude_fields, count, offset, extra_params):
"""List survey reports"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/reporting/surveys"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
"count": count,
"offset": offset,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@reporting_group.command("get-reporting-surveys-id")
@click.argument("SURVEY_ID")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_get_reporting_surveys_id(ctx, survey_id, fields, exclude_fields, extra_params):
"""Get survey report"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/reporting/surveys/{survey_id}"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@reporting_group.command("list-reporting-surveys-id-questions")
@click.argument("SURVEY_ID")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_list_reporting_surveys_id_questions(ctx, survey_id, fields, exclude_fields, extra_params):
"""List survey question reports"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/reporting/surveys/{survey_id}/questions"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@reporting_group.command("get-reporting-surveys-id-questions-id")
@click.argument("SURVEY_ID")
@click.argument("QUESTION_ID")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_get_reporting_surveys_id_questions_id(ctx, survey_id, question_id, fields, exclude_fields, extra_params):
"""Get survey question report"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/reporting/surveys/{survey_id}/questions/{question_id}"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@reporting_group.command("list-reporting-surveys-id-questions-id-answers")
@click.argument("SURVEY_ID")
@click.argument("QUESTION_ID")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--respondent-familiarity-is", "respondent_familiarity_is", default=None, help="Filter survey responses by familiarity of the respondents.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_list_reporting_surveys_id_questions_id_answers(ctx, survey_id, question_id, fields, exclude_fields, respondent_familiarity_is, extra_params):
"""List answers for question"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/reporting/surveys/{survey_id}/questions/{question_id}/answers"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
"respondent_familiarity_is": respondent_familiarity_is,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@reporting_group.command("list-reporting-surveys-id-responses")
@click.argument("SURVEY_ID")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--answered-question", "answered_question", default=None, type=int, help="The ID of the question that was answered.")
@click.option("--chose-answer", "chose_answer", default=None, help="The ID of the option chosen to filter responses on.")
@click.option("--respondent-familiarity-is", "respondent_familiarity_is", default=None, help="Filter survey responses by familiarity of the respondents.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_list_reporting_surveys_id_responses(ctx, survey_id, fields, exclude_fields, answered_question, chose_answer, respondent_familiarity_is, extra_params):
"""List survey responses"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/reporting/surveys/{survey_id}/responses"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
"answered_question": answered_question,
"chose_answer": chose_answer,
"respondent_familiarity_is": respondent_familiarity_is,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@reporting_group.command("get-reporting-surveys-id-responses-id")
@click.argument("SURVEY_ID")
@click.argument("RESPONSE_ID")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_get_reporting_surveys_id_responses_id(ctx, survey_id, response_id, extra_params):
"""Get survey response"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/reporting/surveys/{survey_id}/responses/{response_id}"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@@ -0,0 +1,656 @@
"""Commands for the Mailchimp /reports API resource.
Auto-generated by cli_anything/mailchimp/_codegen/generate.py.
Do not edit manually — re-run the generator to update.
"""
import json
import click
def _parse_json_option(value, option_name, require_object=False):
try:
parsed = json.loads(value)
except json.JSONDecodeError as exc:
raise click.BadParameter(
f"must be valid JSON ({exc.msg})",
param_hint=option_name,
) from exc
if require_object and not isinstance(parsed, dict):
raise click.BadParameter("must be a JSON object", param_hint=option_name)
return parsed
@click.group("reports")
@click.pass_context
def reports_group(ctx):
"""reports resource commands."""
@reports_group.command("list")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--count", "count", default=None, type=int, help="The number of records to return. Default value is 10. Maximum value is 1000")
@click.option("--offset", "offset", default=None, type=int, help="Used for [pagination](https://mailchimp.com/developer/marketing/docs/methods-parameters/#pagination), this is the number of records from a collection to skip. Default value is 0.")
@click.option("--type", "type_", default=None, help="The campaign type.")
@click.option("--before-send-time", "before_send_time", default=None, help="Restrict the response to campaigns sent before the set time. Uses ISO 8601 time format: 2015-10-21T15:41:36+00:00.")
@click.option("--since-send-time", "since_send_time", default=None, help="Restrict the response to campaigns sent after the set time. Uses ISO 8601 time format: 2015-10-21T15:41:36+00:00.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_list_(ctx, fields, exclude_fields, count, offset, type_, before_send_time, since_send_time, extra_params):
"""List campaign reports"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/reports"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
"count": count,
"offset": offset,
"type": type_,
"before_send_time": before_send_time,
"since_send_time": since_send_time,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@reports_group.command("get")
@click.argument("CAMPAIGN_ID")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_get(ctx, campaign_id, fields, exclude_fields, extra_params):
"""Get campaign report"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/reports/{campaign_id}"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@reports_group.command("list-reports-id-abuse-reports-id")
@click.argument("CAMPAIGN_ID")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_list_reports_id_abuse_reports_id(ctx, campaign_id, fields, exclude_fields, extra_params):
"""List abuse reports"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/reports/{campaign_id}/abuse-reports"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@reports_group.command("get-reports-id-abuse-reports-id-id")
@click.argument("CAMPAIGN_ID")
@click.argument("REPORT_ID")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_get_reports_id_abuse_reports_id_id(ctx, campaign_id, report_id, fields, exclude_fields, extra_params):
"""Get abuse report"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/reports/{campaign_id}/abuse-reports/{report_id}"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@reports_group.command("list-reports-id-advice")
@click.argument("CAMPAIGN_ID")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_list_reports_id_advice(ctx, campaign_id, fields, exclude_fields, extra_params):
"""List campaign feedback"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/reports/{campaign_id}/advice"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@reports_group.command("list-reports-id-click-details")
@click.argument("CAMPAIGN_ID")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--count", "count", default=None, type=int, help="The number of records to return. Default value is 10. Maximum value is 1000")
@click.option("--offset", "offset", default=None, type=int, help="Used for [pagination](https://mailchimp.com/developer/marketing/docs/methods-parameters/#pagination), this is the number of records from a collection to skip. Default value is 0.")
@click.option("--sort-field", "sort_field", default=None, help="Returns click reports sorted by the specified field.")
@click.option("--sort-dir", "sort_dir", default=None, help="Determines the order direction for sorted results.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_list_reports_id_click_details(ctx, campaign_id, fields, exclude_fields, count, offset, sort_field, sort_dir, extra_params):
"""List campaign details"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/reports/{campaign_id}/click-details"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
"count": count,
"offset": offset,
"sort_field": sort_field,
"sort_dir": sort_dir,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
reports_group.add_command(_cmd_list_reports_id_click_details, "list-click-details")
@reports_group.command("get-reports-id-click-details-id")
@click.argument("CAMPAIGN_ID")
@click.argument("LINK_ID")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_get_reports_id_click_details_id(ctx, campaign_id, link_id, fields, exclude_fields, extra_params):
"""Get campaign link details"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/reports/{campaign_id}/click-details/{link_id}"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@reports_group.command("list-reports-id-click-details-id-members")
@click.argument("CAMPAIGN_ID")
@click.argument("LINK_ID")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--count", "count", default=None, type=int, help="The number of records to return. Default value is 10. Maximum value is 1000")
@click.option("--offset", "offset", default=None, type=int, help="Used for [pagination](https://mailchimp.com/developer/marketing/docs/methods-parameters/#pagination), this is the number of records from a collection to skip. Default value is 0.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_list_reports_id_click_details_id_members(ctx, campaign_id, link_id, fields, exclude_fields, count, offset, extra_params):
"""List clicked link subscribers"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/reports/{campaign_id}/click-details/{link_id}/members"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
"count": count,
"offset": offset,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@reports_group.command("get-reports-id-click-details-id-members-id")
@click.argument("CAMPAIGN_ID")
@click.argument("LINK_ID")
@click.argument("SUBSCRIBER_HASH")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_get_reports_id_click_details_id_members_id(ctx, campaign_id, link_id, subscriber_hash, fields, exclude_fields, extra_params):
"""Get clicked link subscriber"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/reports/{campaign_id}/click-details/{link_id}/members/{subscriber_hash}"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@reports_group.command("list-reports-id-domain-performance")
@click.argument("CAMPAIGN_ID")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_list_reports_id_domain_performance(ctx, campaign_id, fields, exclude_fields, extra_params):
"""List domain performance stats"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/reports/{campaign_id}/domain-performance"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
reports_group.add_command(_cmd_list_reports_id_domain_performance, "list-domain-performance")
@reports_group.command("list-reports-id-ecommerce-product-activity")
@click.argument("CAMPAIGN_ID")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--count", "count", default=None, type=int, help="The number of records to return. Default value is 10. Maximum value is 1000")
@click.option("--offset", "offset", default=None, type=int, help="Used for [pagination](https://mailchimp.com/developer/marketing/docs/methods-parameters/#pagination), this is the number of records from a collection to skip. Default value is 0.")
@click.option("--sort-field", "sort_field", default=None, help="Returns files sorted by the specified field.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_list_reports_id_ecommerce_product_activity(ctx, campaign_id, fields, exclude_fields, count, offset, sort_field, extra_params):
"""List campaign product activity"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/reports/{campaign_id}/ecommerce-product-activity"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
"count": count,
"offset": offset,
"sort_field": sort_field,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@reports_group.command("list-reports-id-eepurl")
@click.argument("CAMPAIGN_ID")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_list_reports_id_eepurl(ctx, campaign_id, fields, exclude_fields, extra_params):
"""List EepURL activity"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/reports/{campaign_id}/eepurl"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@reports_group.command("list-reports-id-email-activity")
@click.argument("CAMPAIGN_ID")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--count", "count", default=None, type=int, help="The number of records to return. Default value is 10. Maximum value is 1000")
@click.option("--offset", "offset", default=None, type=int, help="Used for [pagination](https://mailchimp.com/developer/marketing/docs/methods-parameters/#pagination), this is the number of records from a collection to skip. Default value is 0.")
@click.option("--since", "since", default=None, help="Restrict results to email activity events that occur after a specific time. Uses ISO 8601 time format: 2015-10-21T15:41:36+00:00.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_list_reports_id_email_activity(ctx, campaign_id, fields, exclude_fields, count, offset, since, extra_params):
"""List email activity"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/reports/{campaign_id}/email-activity"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
"count": count,
"offset": offset,
"since": since,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
reports_group.add_command(_cmd_list_reports_id_email_activity, "list-email-activity")
@reports_group.command("get-reports-id-email-activity-id")
@click.argument("CAMPAIGN_ID")
@click.argument("SUBSCRIBER_HASH")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--since", "since", default=None, help="Restrict results to email activity events that occur after a specific time. Uses ISO 8601 time format: 2015-10-21T15:41:36+00:00.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_get_reports_id_email_activity_id(ctx, campaign_id, subscriber_hash, fields, exclude_fields, since, extra_params):
"""Get subscriber email activity"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/reports/{campaign_id}/email-activity/{subscriber_hash}"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
"since": since,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@reports_group.command("list-reports-id-locations")
@click.argument("CAMPAIGN_ID")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--count", "count", default=None, type=int, help="The number of records to return. Default value is 10. Maximum value is 1000")
@click.option("--offset", "offset", default=None, type=int, help="Used for [pagination](https://mailchimp.com/developer/marketing/docs/methods-parameters/#pagination), this is the number of records from a collection to skip. Default value is 0.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_list_reports_id_locations(ctx, campaign_id, fields, exclude_fields, count, offset, extra_params):
"""List top open activities"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/reports/{campaign_id}/locations"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
"count": count,
"offset": offset,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
reports_group.add_command(_cmd_list_reports_id_locations, "list-locations")
@reports_group.command("list-reports-id-open-details")
@click.argument("CAMPAIGN_ID")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--count", "count", default=None, type=int, help="The number of records to return. Default value is 10. Maximum value is 1000")
@click.option("--offset", "offset", default=None, type=int, help="Used for [pagination](https://mailchimp.com/developer/marketing/docs/methods-parameters/#pagination), this is the number of records from a collection to skip. Default value is 0.")
@click.option("--since", "since", default=None, help="Restrict results to campaign open events that occur after a specific time. Uses ISO 8601 time format: 2015-10-21T15:41:36+00:00.")
@click.option("--sort-field", "sort_field", default=None, help="Returns open reports sorted by the specified field.")
@click.option("--sort-dir", "sort_dir", default=None, help="Determines the order direction for sorted results.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_list_reports_id_open_details(ctx, campaign_id, fields, exclude_fields, count, offset, since, sort_field, sort_dir, extra_params):
"""List campaign open details"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/reports/{campaign_id}/open-details"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
"count": count,
"offset": offset,
"since": since,
"sort_field": sort_field,
"sort_dir": sort_dir,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
reports_group.add_command(_cmd_list_reports_id_open_details, "list-open-details")
@reports_group.command("get-reports-id-open-details-id-members-id")
@click.argument("CAMPAIGN_ID")
@click.argument("SUBSCRIBER_HASH")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_get_reports_id_open_details_id_members_id(ctx, campaign_id, subscriber_hash, fields, exclude_fields, extra_params):
"""Get opened campaign subscriber"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/reports/{campaign_id}/open-details/{subscriber_hash}"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@reports_group.command("list-reports-id-sent-to")
@click.argument("CAMPAIGN_ID")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--count", "count", default=None, type=int, help="The number of records to return. Default value is 10. Maximum value is 1000")
@click.option("--offset", "offset", default=None, type=int, help="Used for [pagination](https://mailchimp.com/developer/marketing/docs/methods-parameters/#pagination), this is the number of records from a collection to skip. Default value is 0.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_list_reports_id_sent_to(ctx, campaign_id, fields, exclude_fields, count, offset, extra_params):
"""List campaign recipients"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/reports/{campaign_id}/sent-to"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
"count": count,
"offset": offset,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@reports_group.command("get-reports-id-sent-to-id")
@click.argument("CAMPAIGN_ID")
@click.argument("SUBSCRIBER_HASH")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_get_reports_id_sent_to_id(ctx, campaign_id, subscriber_hash, fields, exclude_fields, extra_params):
"""Get campaign recipient info"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/reports/{campaign_id}/sent-to/{subscriber_hash}"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@reports_group.command("list-reports-id-sub-reports-id")
@click.argument("CAMPAIGN_ID")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_list_reports_id_sub_reports_id(ctx, campaign_id, fields, exclude_fields, extra_params):
"""List child campaign reports"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/reports/{campaign_id}/sub-reports"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@reports_group.command("list-reports-id-unsubscribed")
@click.argument("CAMPAIGN_ID")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--count", "count", default=None, type=int, help="The number of records to return. Default value is 10. Maximum value is 1000")
@click.option("--offset", "offset", default=None, type=int, help="Used for [pagination](https://mailchimp.com/developer/marketing/docs/methods-parameters/#pagination), this is the number of records from a collection to skip. Default value is 0.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_list_reports_id_unsubscribed(ctx, campaign_id, fields, exclude_fields, count, offset, extra_params):
"""List unsubscribed members"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/reports/{campaign_id}/unsubscribed"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
"count": count,
"offset": offset,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
reports_group.add_command(_cmd_list_reports_id_unsubscribed, "list-unsubscribed")
@reports_group.command("get-reports-id-unsubscribed-id")
@click.argument("CAMPAIGN_ID")
@click.argument("SUBSCRIBER_HASH")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_get_reports_id_unsubscribed_id(ctx, campaign_id, subscriber_hash, fields, exclude_fields, extra_params):
"""Get unsubscribed member"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/reports/{campaign_id}/unsubscribed/{subscriber_hash}"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@@ -0,0 +1,52 @@
"""Commands for the Mailchimp /root API resource.
Auto-generated by cli_anything/mailchimp/_codegen/generate.py.
Do not edit manually — re-run the generator to update.
"""
import json
import click
def _parse_json_option(value, option_name, require_object=False):
try:
parsed = json.loads(value)
except json.JSONDecodeError as exc:
raise click.BadParameter(
f"must be valid JSON ({exc.msg})",
param_hint=option_name,
) from exc
if require_object and not isinstance(parsed, dict):
raise click.BadParameter("must be a JSON object", param_hint=option_name)
return parsed
@click.group("root")
@click.pass_context
def root_group(ctx):
"""root resource commands."""
@root_group.command("list")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_list_(ctx, fields, exclude_fields, extra_params):
"""List api root resources"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@@ -0,0 +1,54 @@
"""Commands for the Mailchimp /search-campaigns API resource.
Auto-generated by cli_anything/mailchimp/_codegen/generate.py.
Do not edit manually — re-run the generator to update.
"""
import json
import click
def _parse_json_option(value, option_name, require_object=False):
try:
parsed = json.loads(value)
except json.JSONDecodeError as exc:
raise click.BadParameter(
f"must be valid JSON ({exc.msg})",
param_hint=option_name,
) from exc
if require_object and not isinstance(parsed, dict):
raise click.BadParameter("must be a JSON object", param_hint=option_name)
return parsed
@click.group("search-campaigns")
@click.pass_context
def search_campaigns_group(ctx):
"""search-campaigns resource commands."""
@search_campaigns_group.command("list")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--query", "query", default=None, help="The search query used to filter results.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_list_(ctx, fields, exclude_fields, query, extra_params):
"""Search campaigns"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/search-campaigns"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
"query": query,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@@ -0,0 +1,56 @@
"""Commands for the Mailchimp /search-members API resource.
Auto-generated by cli_anything/mailchimp/_codegen/generate.py.
Do not edit manually — re-run the generator to update.
"""
import json
import click
def _parse_json_option(value, option_name, require_object=False):
try:
parsed = json.loads(value)
except json.JSONDecodeError as exc:
raise click.BadParameter(
f"must be valid JSON ({exc.msg})",
param_hint=option_name,
) from exc
if require_object and not isinstance(parsed, dict):
raise click.BadParameter("must be a JSON object", param_hint=option_name)
return parsed
@click.group("search-members")
@click.pass_context
def search_members_group(ctx):
"""search-members resource commands."""
@search_members_group.command("list")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--query", "query", default=None, help="The search query used to filter results. Query should be a valid email, or a string representing a contact's first or last name.")
@click.option("--list-id", "list_id", default=None, help="The unique id for the list.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_list_(ctx, fields, exclude_fields, query, list_id, extra_params):
"""Search members"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/search-members"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
"query": query,
"list_id": list_id,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@@ -0,0 +1,253 @@
"""Commands for the Mailchimp /sms-campaigns API resource.
Auto-generated by cli_anything/mailchimp/_codegen/generate.py.
Do not edit manually — re-run the generator to update.
"""
import json
import click
def _parse_json_option(value, option_name, require_object=False):
try:
parsed = json.loads(value)
except json.JSONDecodeError as exc:
raise click.BadParameter(
f"must be valid JSON ({exc.msg})",
param_hint=option_name,
) from exc
if require_object and not isinstance(parsed, dict):
raise click.BadParameter("must be a JSON object", param_hint=option_name)
return parsed
@click.group("sms-campaigns")
@click.pass_context
def sms_campaigns_group(ctx):
"""sms-campaigns resource commands."""
@sms_campaigns_group.command("list")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--count", "count", default=None, type=int, help="The number of records to return. Default value is 10. Maximum value is 1000")
@click.option("--offset", "offset", default=None, type=int, help="Used for [pagination](https://mailchimp.com/developer/marketing/docs/methods-parameters/#pagination), this is the number of records from a collection to skip. Default value is 0.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_list_(ctx, fields, exclude_fields, count, offset, extra_params):
"""List SMS campaigns"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/sms-campaigns"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
"count": count,
"offset": offset,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@sms_campaigns_group.command("create")
@click.option("--data", default=None, help="Request body as JSON string.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_create(ctx, data, extra_params):
"""Add SMS campaign"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/sms-campaigns"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
body = _parse_json_option(data, "--data") if data else None
client = get_client()
try:
result = client.post(path, json=body, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@sms_campaigns_group.command("delete")
@click.argument("SMS_CAMPAIGN_ID")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_delete(ctx, sms_campaign_id, extra_params):
"""Delete SMS campaign"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/sms-campaigns/{sms_campaign_id}"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.delete(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out_ok("Deleted.")
@sms_campaigns_group.command("get")
@click.argument("SMS_CAMPAIGN_ID")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_get(ctx, sms_campaign_id, fields, exclude_fields, extra_params):
"""Get SMS campaign info"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/sms-campaigns/{sms_campaign_id}"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@sms_campaigns_group.command("update")
@click.argument("SMS_CAMPAIGN_ID")
@click.option("--data", default=None, help="Request body as JSON string.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_update(ctx, sms_campaign_id, data, extra_params):
"""Update SMS campaign settings"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/sms-campaigns/{sms_campaign_id}"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
body = _parse_json_option(data, "--data") if data else None
client = get_client()
try:
result = client.patch(path, json=body, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@sms_campaigns_group.command("cancel-send")
@click.argument("SMS_CAMPAIGN_ID")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_cancel_send(ctx, sms_campaign_id, extra_params):
"""Cancel SMS campaign send"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/sms-campaigns/{sms_campaign_id}/actions/cancel-send"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.post(path, json=None, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@sms_campaigns_group.command("schedule")
@click.argument("SMS_CAMPAIGN_ID")
@click.option("--data", default=None, help="Request body as JSON string.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_schedule(ctx, sms_campaign_id, data, extra_params):
"""Schedule SMS campaign"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/sms-campaigns/{sms_campaign_id}/actions/schedule"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
body = _parse_json_option(data, "--data") if data else None
client = get_client()
try:
result = client.post(path, json=body, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@sms_campaigns_group.command("send")
@click.argument("SMS_CAMPAIGN_ID")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_send(ctx, sms_campaign_id, extra_params):
"""Send SMS campaign"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/sms-campaigns/{sms_campaign_id}/actions/send"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.post(path, json=None, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@sms_campaigns_group.command("list-sms-campaigns-id-content")
@click.argument("SMS_CAMPAIGN_ID")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_list_sms_campaigns_id_content(ctx, sms_campaign_id, fields, exclude_fields, extra_params):
"""Get SMS campaign content"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/sms-campaigns/{sms_campaign_id}/content"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@sms_campaigns_group.command("update-sms-campaigns-id-content")
@click.argument("SMS_CAMPAIGN_ID")
@click.option("--data", default=None, help="Request body as JSON string.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_update_sms_campaigns_id_content(ctx, sms_campaign_id, data, extra_params):
"""Set SMS campaign content"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/sms-campaigns/{sms_campaign_id}/content"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
body = _parse_json_option(data, "--data") if data else None
client = get_client()
try:
result = client.put(path, json=body, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@@ -0,0 +1,91 @@
"""Commands for the Mailchimp /surveys API resource.
Auto-generated by cli_anything/mailchimp/_codegen/generate.py.
Do not edit manually — re-run the generator to update.
"""
import json
import click
def _parse_json_option(value, option_name, require_object=False):
try:
parsed = json.loads(value)
except json.JSONDecodeError as exc:
raise click.BadParameter(
f"must be valid JSON ({exc.msg})",
param_hint=option_name,
) from exc
if require_object and not isinstance(parsed, dict):
raise click.BadParameter("must be a JSON object", param_hint=option_name)
return parsed
@click.group("surveys")
@click.pass_context
def surveys_group(ctx):
"""surveys resource commands."""
@surveys_group.command("create-email")
@click.argument("LIST_ID")
@click.argument("SURVEY_ID")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_create_email(ctx, list_id, survey_id, extra_params):
"""Create a Survey Campaign"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/lists/{list_id}/surveys/{survey_id}/actions/create-email"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.post(path, json=None, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@surveys_group.command("publish")
@click.argument("LIST_ID")
@click.argument("SURVEY_ID")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_publish(ctx, list_id, survey_id, extra_params):
"""Publish a Survey"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/lists/{list_id}/surveys/{survey_id}/actions/publish"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.post(path, json=None, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@surveys_group.command("unpublish")
@click.argument("LIST_ID")
@click.argument("SURVEY_ID")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_unpublish(ctx, list_id, survey_id, extra_params):
"""Unpublish a Survey"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/lists/{list_id}/surveys/{survey_id}/actions/unpublish"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.post(path, json=None, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@@ -0,0 +1,144 @@
"""Commands for the Mailchimp /template-folders API resource.
Auto-generated by cli_anything/mailchimp/_codegen/generate.py.
Do not edit manually — re-run the generator to update.
"""
import json
import click
def _parse_json_option(value, option_name, require_object=False):
try:
parsed = json.loads(value)
except json.JSONDecodeError as exc:
raise click.BadParameter(
f"must be valid JSON ({exc.msg})",
param_hint=option_name,
) from exc
if require_object and not isinstance(parsed, dict):
raise click.BadParameter("must be a JSON object", param_hint=option_name)
return parsed
@click.group("template-folders")
@click.pass_context
def template_folders_group(ctx):
"""template-folders resource commands."""
@template_folders_group.command("list")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--count", "count", default=None, type=int, help="The number of records to return. Default value is 10. Maximum value is 1000")
@click.option("--offset", "offset", default=None, type=int, help="Used for [pagination](https://mailchimp.com/developer/marketing/docs/methods-parameters/#pagination), this is the number of records from a collection to skip. Default value is 0.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_list_(ctx, fields, exclude_fields, count, offset, extra_params):
"""List template folders"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/template-folders"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
"count": count,
"offset": offset,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@template_folders_group.command("create")
@click.option("--data", default=None, help="Request body as JSON string.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_create(ctx, data, extra_params):
"""Add template folder"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/template-folders"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
body = _parse_json_option(data, "--data") if data else None
client = get_client()
try:
result = client.post(path, json=body, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@template_folders_group.command("delete")
@click.argument("FOLDER_ID")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_delete(ctx, folder_id, extra_params):
"""Delete template folder"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/template-folders/{folder_id}"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.delete(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out_ok("Deleted.")
@template_folders_group.command("get")
@click.argument("FOLDER_ID")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_get(ctx, folder_id, fields, exclude_fields, extra_params):
"""Get template folder"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/template-folders/{folder_id}"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@template_folders_group.command("update")
@click.argument("FOLDER_ID")
@click.option("--data", default=None, help="Request body as JSON string.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_update(ctx, folder_id, data, extra_params):
"""Update template folder"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/template-folders/{folder_id}"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
body = _parse_json_option(data, "--data") if data else None
client = get_client()
try:
result = client.patch(path, json=body, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@@ -0,0 +1,187 @@
"""Commands for the Mailchimp /templates API resource.
Auto-generated by cli_anything/mailchimp/_codegen/generate.py.
Do not edit manually — re-run the generator to update.
"""
import json
import click
def _parse_json_option(value, option_name, require_object=False):
try:
parsed = json.loads(value)
except json.JSONDecodeError as exc:
raise click.BadParameter(
f"must be valid JSON ({exc.msg})",
param_hint=option_name,
) from exc
if require_object and not isinstance(parsed, dict):
raise click.BadParameter("must be a JSON object", param_hint=option_name)
return parsed
@click.group("templates")
@click.pass_context
def templates_group(ctx):
"""templates resource commands."""
@templates_group.command("list")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--count", "count", default=None, type=int, help="The number of records to return. Default value is 10. Maximum value is 1000")
@click.option("--offset", "offset", default=None, type=int, help="Used for [pagination](https://mailchimp.com/developer/marketing/docs/methods-parameters/#pagination), this is the number of records from a collection to skip. Default value is 0.")
@click.option("--created-by", "created_by", default=None, help="The Mailchimp account user who created the template.")
@click.option("--since-date-created", "since_date_created", default=None, help="Restrict the response to templates created after the set date. Uses ISO 8601 time format: 2015-10-21T15:41:36+00:00.")
@click.option("--before-date-created", "before_date_created", default=None, help="Restrict the response to templates created before the set date. Uses ISO 8601 time format: 2015-10-21T15:41:36+00:00.")
@click.option("--type", "type_", default=None, help="Limit results based on template type.")
@click.option("--category", "category", default=None, help="Limit results based on category.")
@click.option("--folder-id", "folder_id", default=None, help="The unique folder id.")
@click.option("--sort-field", "sort_field", default=None, help="Returns user templates sorted by the specified field.")
@click.option("--content-type", "content_type", default=None, help="Limit results based on how the template's content is put together. Only templates of type `user` can be filtered by `content_type`. If you want to retrieve saved templates created with the legacy email editor, then filter `content_type` to `template`. If you'd rather pull your saved templates for the new editor, filter to `multichannel`. For code your own templates, filter to `html`.")
@click.option("--sort-dir", "sort_dir", default=None, help="Determines the order direction for sorted results.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_list_(ctx, fields, exclude_fields, count, offset, created_by, since_date_created, before_date_created, type_, category, folder_id, sort_field, content_type, sort_dir, extra_params):
"""List templates"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/templates"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
"count": count,
"offset": offset,
"created_by": created_by,
"since_date_created": since_date_created,
"before_date_created": before_date_created,
"type": type_,
"category": category,
"folder_id": folder_id,
"sort_field": sort_field,
"content_type": content_type,
"sort_dir": sort_dir,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@templates_group.command("create")
@click.option("--data", default=None, help="Request body as JSON string.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_create(ctx, data, extra_params):
"""Add template"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/templates"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
body = _parse_json_option(data, "--data") if data else None
client = get_client()
try:
result = client.post(path, json=body, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@templates_group.command("delete")
@click.argument("TEMPLATE_ID")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_delete(ctx, template_id, extra_params):
"""Delete template"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/templates/{template_id}"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.delete(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out_ok("Deleted.")
@templates_group.command("get")
@click.argument("TEMPLATE_ID")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_get(ctx, template_id, fields, exclude_fields, extra_params):
"""Get template info"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/templates/{template_id}"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@templates_group.command("update")
@click.argument("TEMPLATE_ID")
@click.option("--data", default=None, help="Request body as JSON string.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_update(ctx, template_id, data, extra_params):
"""Update template"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/templates/{template_id}"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
body = _parse_json_option(data, "--data") if data else None
client = get_client()
try:
result = client.patch(path, json=body, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@templates_group.command("list-templates-id-default-content")
@click.argument("TEMPLATE_ID")
@click.option("--fields", "fields", default=None, help="A comma-separated list of fields to return. Reference parameters of sub-objects with dot notation.")
@click.option("--exclude-fields", "exclude_fields", default=None, help="A comma-separated list of fields to exclude. Reference parameters of sub-objects with dot notation.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_list_templates_id_default_content(ctx, template_id, fields, exclude_fields, extra_params):
"""View default content"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/templates/{template_id}/default-content"
params = {k: v for k, v in {
"fields": fields,
"exclude_fields": exclude_fields,
}.items() if v is not None}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@@ -0,0 +1,130 @@
"""Commands for the Mailchimp /verified-domains API resource.
Auto-generated by cli_anything/mailchimp/_codegen/generate.py.
Do not edit manually — re-run the generator to update.
"""
import json
import click
def _parse_json_option(value, option_name, require_object=False):
try:
parsed = json.loads(value)
except json.JSONDecodeError as exc:
raise click.BadParameter(
f"must be valid JSON ({exc.msg})",
param_hint=option_name,
) from exc
if require_object and not isinstance(parsed, dict):
raise click.BadParameter("must be a JSON object", param_hint=option_name)
return parsed
@click.group("verified-domains")
@click.pass_context
def verified_domains_group(ctx):
"""verified-domains resource commands."""
@verified_domains_group.command("list")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_list_(ctx, extra_params):
"""List sending domains"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/verified-domains"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@verified_domains_group.command("create")
@click.option("--data", default=None, help="Request body as JSON string.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_create(ctx, data, extra_params):
"""Add domain to account"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/verified-domains"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
body = _parse_json_option(data, "--data") if data else None
client = get_client()
try:
result = client.post(path, json=body, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@verified_domains_group.command("delete")
@click.argument("DOMAIN_NAME")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_delete(ctx, domain_name, extra_params):
"""Delete domain"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/verified-domains/{domain_name}"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.delete(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out_ok("Deleted.")
@verified_domains_group.command("get")
@click.argument("DOMAIN_NAME")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_get(ctx, domain_name, extra_params):
"""Get domain info"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/verified-domains/{domain_name}"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
client = get_client()
try:
result = client.get(path, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@verified_domains_group.command("verify")
@click.argument("DOMAIN_NAME")
@click.option("--data", default=None, help="Request body as JSON string.")
@click.option("--extra-params", default=None, help="Extra query params as JSON object, e.g. '{\"key\":\"val\"}'")
@click.pass_context
def _cmd_verify(ctx, domain_name, data, extra_params):
"""Verify domain"""
from cli_anything.mailchimp.core.client import get_client, MailchimpError
from cli_anything.mailchimp.utils.output import _out, _out_ok, _out_err
path = f"/verified-domains/{domain_name}/actions/verify"
params = {}
if extra_params:
params.update(_parse_json_option(extra_params, "--extra-params", require_object=True))
body = _parse_json_option(data, "--data") if data else None
client = get_client()
try:
result = client.post(path, json=body, params=params or None)
except MailchimpError as e:
_out_err(e.status, e.title, e.detail, e.raw)
return
_out(result)
@@ -0,0 +1,128 @@
"""Mailchimp Marketing API v3.0 HTTP client."""
from __future__ import annotations
import hashlib
import os
import sys
from typing import Any
import requests
_BASE = "https://{dc}.api.mailchimp.com/3.0"
_DEFAULT_TIMEOUT = 30
def _server_prefix(api_key: str) -> str:
"""Derive the data-centre prefix from the API key suffix (e.g. 'us8')."""
if "-" not in api_key:
raise ValueError(
"MAILCHIMP_API_KEY must include the data-centre suffix, "
"e.g. 'abc123-us8'."
)
return api_key.rsplit("-", 1)[-1]
def subscriber_hash(email: str) -> str:
"""MD5 hash of the lowercased email — Mailchimp's subscriber identifier."""
return hashlib.md5(email.strip().lower().encode()).hexdigest()
class MailchimpAuthError(Exception):
"""Raised when no API key is available."""
class MailchimpError(Exception):
def __init__(self, status: int, title: str, detail: str, raw: dict):
super().__init__(f"HTTP {status}: {title} — {detail}")
self.status = status
self.title = title
self.detail = detail
self.raw = raw
class MailchimpClient:
def __init__(self, api_key: str | None = None):
key = api_key or os.environ.get("MAILCHIMP_API_KEY", "")
if not key:
raise MailchimpAuthError(
"MAILCHIMP_API_KEY is not set. "
"Export it before use: export MAILCHIMP_API_KEY=<key>-<dc>"
)
self._key = key
try:
self._dc = _server_prefix(key)
except ValueError as e:
raise MailchimpAuthError(str(e)) from e
self._base = _BASE.format(dc=self._dc)
self._session = requests.Session()
self._session.auth = ("anystring", key)
self._session.headers.update({"User-Agent": "cli-anything-mailchimp/0.1.0"})
# ── Low-level request helpers ──────────────────────────────────────
def _url(self, path: str) -> str:
return f"{self._base}/{path.lstrip('/')}"
def _raise(self, resp: requests.Response) -> None:
if resp.ok:
return
try:
body = resp.json()
raise MailchimpError(
status=resp.status_code,
title=body.get("title", resp.reason),
detail=body.get("detail", ""),
raw=body,
)
except ValueError:
raise MailchimpError(
status=resp.status_code,
title=resp.reason,
detail=resp.text[:200],
raw={},
)
def get(self, path: str, params: dict | None = None) -> Any:
resp = self._session.get(self._url(path), params=params, timeout=_DEFAULT_TIMEOUT)
self._raise(resp)
return resp.json()
def post(self, path: str, json: dict | None = None, params: dict | None = None) -> Any:
resp = self._session.post(
self._url(path), json=json, params=params, timeout=_DEFAULT_TIMEOUT
)
self._raise(resp)
if resp.status_code == 204:
return {}
return resp.json()
def patch(self, path: str, json: dict | None = None, params: dict | None = None) -> Any:
resp = self._session.patch(
self._url(path), json=json, params=params, timeout=_DEFAULT_TIMEOUT
)
self._raise(resp)
return resp.json()
def put(self, path: str, json: dict | None = None, params: dict | None = None) -> Any:
resp = self._session.put(
self._url(path), json=json, params=params, timeout=_DEFAULT_TIMEOUT
)
self._raise(resp)
return resp.json()
def delete(self, path: str, params: dict | None = None) -> dict:
resp = self._session.delete(
self._url(path), params=params, timeout=_DEFAULT_TIMEOUT
)
self._raise(resp)
return {"ok": True}
def get_client() -> MailchimpClient:
"""Return a client instance, printing an error and exiting if key is missing."""
try:
return MailchimpClient()
except MailchimpAuthError as e:
print(f"Error: {e}", file=sys.stderr)
raise SystemExit(1)
@@ -0,0 +1,68 @@
"""Generic count/offset paginator for the Mailchimp API."""
from __future__ import annotations
from typing import Generator
_PAGE_SIZE = 1000
def paginate(
client,
path: str,
result_key: str,
params: dict | None = None,
page_size: int = _PAGE_SIZE,
) -> Generator[dict, None, None]:
"""Yield all items from a paginated Mailchimp endpoint.
Args:
client: MailchimpClient instance.
path: API path, e.g. "/lists".
result_key: Key inside the response that holds the item array.
params: Extra query params (count/offset will be added automatically).
page_size: Items per page.
"""
params = dict(params or {})
params["count"] = page_size
offset = 0
total: int | None = None
while True:
params["offset"] = offset
data = client.get(path, params=params)
items: list = data.get(result_key, [])
if total is None:
total = data.get("total_items", None)
yield from items
offset += len(items)
# Stop when we have fetched all items or got an empty page
if not items or (total is not None and offset >= total):
break
def collect(
client,
path: str,
result_key: str,
params: dict | None = None,
page_size: int = _PAGE_SIZE,
) -> tuple[list[dict], int]:
"""Return (items, total_items) — fetches a single page by default.
When the caller passes explicit count/offset in params those are respected;
otherwise a single page of page_size is fetched.
"""
params = dict(params or {})
if "count" not in params:
params["count"] = page_size
if "offset" not in params:
params["offset"] = 0
data = client.get(path, params=params)
items = data.get(result_key, [])
total = data.get("total_items", len(items))
return items, total
@@ -0,0 +1,93 @@
"""cli-anything harness for the Mailchimp Marketing API v3.0."""
from __future__ import annotations
import click
import cli_anything.mailchimp.utils.output as _output_mod
from cli_anything.mailchimp.commands import ALL_GROUPS
@click.group(invoke_without_command=True)
@click.option("--json", "use_json", is_flag=True, default=False, help="Output as JSON.")
@click.version_option("0.1.0", prog_name="cli-anything-mailchimp")
@click.pass_context
def cli(ctx: click.Context, use_json: bool) -> None:
"""cli-anything harness for the Mailchimp Marketing API v3.0.
Set MAILCHIMP_API_KEY before use:
export MAILCHIMP_API_KEY=<your-key>-<dc>
Run without arguments to enter the interactive REPL.
"""
ctx.ensure_object(dict)
_output_mod.USE_JSON = use_json
if ctx.invoked_subcommand is None:
_start_repl()
# Register all generated resource groups
for _group in ALL_GROUPS:
cli.add_command(_group)
def main() -> None:
"""Entry point for the Click CLI."""
cli()
def _start_repl() -> None:
"""Interactive REPL using prompt_toolkit."""
from cli_anything.mailchimp.utils.repl_skin import ReplSkin
import shlex
skin = ReplSkin("mailchimp", version="0.1.0")
skin.print_banner()
pt_session = skin.create_prompt_session()
while True:
try:
raw = skin.get_input(pt_session)
except (KeyboardInterrupt, EOFError):
skin.print_goodbye()
break
line = raw.strip()
if not line:
continue
if line in ("quit", "exit", "q"):
skin.print_goodbye()
break
if line in ("help", "?"):
_print_repl_help(skin)
continue
try:
args = shlex.split(line)
except ValueError as e:
skin.error(f"Parse error: {e}")
continue
try:
cli.main(args=args, standalone_mode=False)
except SystemExit:
pass
except click.UsageError as e:
skin.error(str(e))
except Exception as e:
skin.error(str(e))
def _print_repl_help(skin: object) -> None:
from cli_anything.mailchimp.commands import ALL_GROUPS
commands = {g.name: (g.help or "").split("\n")[0] for g in ALL_GROUPS}
commands["--help"] = "Show help for any command"
commands["quit"] = "Exit the REPL"
skin.help(commands) # type: ignore[attr-defined]
if __name__ == "__main__":
main()
@@ -0,0 +1,228 @@
---
name: cli-anything-mailchimp
description: CLI harness for the Mailchimp Marketing API v3.0 — 303 commands across 30 resource groups (lists, campaigns, reports, automations, ecommerce, templates, and more). Supports JSON output and interactive REPL mode.
---
# cli-anything-mailchimp
## Identity
An agent-native CLI for the [Mailchimp Marketing API v3.0](https://mailchimp.com/developer/marketing/docs/fundamentals/), built on the [CLI-Anything](https://github.com/HKUDS/CLI-Anything) framework.
## What This CLI Does
- Manage **audiences (lists)** — create, update, delete lists; add/update/archive members; manage merge fields, segments, tags, and webhooks.
- Run **campaigns** — create, schedule, send, pause, replicate, and analyse email campaigns.
- Read **reports** — open rates, click rates, bounce stats, unsubscribes, email activity, geographic data.
- Control **automations** — create and manage automated email workflows.
- Manage **e-commerce** — stores, orders, customers, products, carts, and promo codes.
- Use **templates, file manager, landing pages, SMS campaigns, surveys**, and all other Marketing API resources.
## Prerequisites
- Python 3.10+
- `MAILCHIMP_API_KEY` environment variable set to your API key (including datacenter suffix, e.g. `abc123-us8`)
## Installation
```bash
# From the CLI-Anything repo (once merged):
pip install git+https://github.com/HKUDS/CLI-Anything.git#subdirectory=mailchimp/agent-harness
# During development:
cd mailchimp/agent-harness && pip install -e .
```
## Command Reference
### Root
| Command | Description |
|---|---|
| `cli-anything-mailchimp ping` | Health check — confirms API connectivity |
| `cli-anything-mailchimp root list` | Get account info |
| `cli-anything-mailchimp --json <cmd>` | Output any command as JSON |
| `cli-anything-mailchimp` | Launch interactive REPL |
### Lists (Audiences)
| Command | Description |
|---|---|
| `lists list` | List all audiences |
| `lists get <LIST_ID>` | Get audience info |
| `lists create --data '<json>'` | Create audience |
| `lists update <LIST_ID> --data '<json>'` | Update audience |
| `lists delete <LIST_ID>` | Delete audience |
| `lists list-lists-id-members <LIST_ID>` | List members |
| `lists get-lists-id-members-id <LIST_ID> <SUBSCRIBER_HASH>` | Get member by MD5 hash |
| `lists create-lists-id-members <LIST_ID> --data '<json>'` | Add member |
| `lists list-lists-id-merge-fields <LIST_ID>` | List merge fields |
| `lists create-lists-id-merge-fields <LIST_ID> --data '<json>'` | Add merge field |
| `lists list-lists-id-segments <LIST_ID>` | List segments |
| `lists list-list-member-tags <LIST_ID> <SUBSCRIBER_HASH>` | List member tags |
| `lists create-list-member-tags <LIST_ID> <SUBSCRIBER_HASH> --data '<json>'` | Add/remove member tags |
| `lists list-lists-id-webhooks <LIST_ID>` | List webhooks |
| `lists create-lists-id-webhooks <LIST_ID> --data '<json>'` | Add webhook |
### Campaigns
| Command | Description |
|---|---|
| `campaigns list` | List campaigns |
| `campaigns get <CAMPAIGN_ID>` | Get campaign info |
| `campaigns create --data '<json>'` | Create campaign |
| `campaigns update <CAMPAIGN_ID> --data '<json>'` | Update campaign settings |
| `campaigns delete <CAMPAIGN_ID>` | Delete campaign |
| `campaigns send <CAMPAIGN_ID>` | Send campaign immediately |
| `campaigns schedule <CAMPAIGN_ID> --data '<json>'` | Schedule campaign |
| `campaigns cancel-send <CAMPAIGN_ID>` | Cancel scheduled send |
| `campaigns pause <CAMPAIGN_ID>` | Pause RSS campaign |
| `campaigns resume <CAMPAIGN_ID>` | Resume RSS campaign |
| `campaigns replicate <CAMPAIGN_ID>` | Duplicate campaign |
| `campaigns list-content <CAMPAIGN_ID>` | Get campaign content |
| `campaigns list-send-checklist <CAMPAIGN_ID>` | Pre-send checklist |
### Reports
| Command | Description |
|---|---|
| `reports list` | List all campaign reports |
| `reports get <CAMPAIGN_ID>` | Get campaign summary report |
| `reports list-email-activity <CAMPAIGN_ID>` | Per-subscriber open/click activity |
| `reports list-click-details <CAMPAIGN_ID>` | Link click breakdown |
| `reports list-open-details <CAMPAIGN_ID>` | Per-subscriber opens |
| `reports list-unsubscribed <CAMPAIGN_ID>` | Unsubscribers |
| `reports list-locations <CAMPAIGN_ID>` | Geographic breakdown |
| `reports list-domain-performance <CAMPAIGN_ID>` | Per-domain stats |
### Automations
| Command | Description |
|---|---|
| `automations list` | List automations |
| `automations get <WORKFLOW_ID>` | Get automation info |
| `automations create --data '<json>'` | Create automation |
| `automations pause <WORKFLOW_ID>` | Pause automation |
| `automations start <WORKFLOW_ID>` | Start automation |
| `automations archive <WORKFLOW_ID>` | Archive automation |
| `automations list-emails <WORKFLOW_ID>` | List automation emails |
### E-commerce
| Command | Description |
|---|---|
| `ecommerce list-ecommerce-stores` | List stores |
| `ecommerce get <STORE_ID>` | Get store info |
| `ecommerce create --data '<json>'` | Add store |
| `ecommerce list-ecommerce-stores-id-orders <STORE_ID>` | List orders |
| `ecommerce list-ecommerce-stores-id-products <STORE_ID>` | List products |
| `ecommerce list-ecommerce-stores-id-customers <STORE_ID>` | List customers |
| `ecommerce list-ecommerce-stores-id-carts <STORE_ID>` | List carts |
| `ecommerce list-ecommerce-stores-id-promocodes <PROMO_RULE_ID> <STORE_ID>` | List promo codes |
### Other Resources
| Group | Description |
|---|---|
| `templates` | Email templates (list, get, create, update, delete) |
| `template-folders` | Template folders |
| `campaign-folders` | Campaign folders |
| `file-manager` | Files and folders in the file manager |
| `landing-pages` | Landing pages (list, create, publish, unpublish) |
| `sms-campaigns` | SMS campaigns (10 operations) |
| `surveys` | Surveys (list, get, publish) |
| `reporting` | Facebook ad and landing page reporting |
| `search-campaigns` | Search campaigns by query |
| `search-members` | Search members across all audiences |
| `batches` | Batch API operations |
| `batch-webhooks` | Batch operation webhooks |
| `verified-domains` | Email domain verification |
| `authorized-apps` | OAuth connected apps |
| `connected-sites` | Connected site integrations |
| `conversations` | Inbox conversations |
| `activity-feed` | Account activity feed |
| `account-exports` | Account data exports |
## JSON Output
All commands support `--json` at the root level:
```bash
# List all audiences as JSON
cli-anything-mailchimp --json lists list
# Get a campaign report as JSON
cli-anything-mailchimp --json reports get abc123def
# Pipe to jq — use the native Mailchimp field name for the resource
cli-anything-mailchimp --json lists list | jq '.lists[].name'
cli-anything-mailchimp --json campaigns list | jq '.campaigns[].id'
```
**Envelope shapes** (native Mailchimp API response — use the resource-specific key):
```json
// List endpoints — key matches the resource name (lists, campaigns, members, etc.)
{"lists": [...], "total_items": 42, "_links": [...]}
{"campaigns": [...], "total_items": 10, "_links": [...]}
// Single resource GET / POST / PATCH
{"id": "abc123", "name": "My List", ...}
// DELETE
{"ok": true, "message": "Deleted."}
// Error
{"ok": false, "message": "Resource Not Found: ...", "data": {...}}
```
## Common Agent Patterns
```bash
# Get account health
cli-anything-mailchimp --json ping | jq '.health_status'
# List all audience IDs and names
cli-anything-mailchimp --json lists list | jq '.lists[] | {id, name}'
# Find all subscribed members in an audience
cli-anything-mailchimp --json lists list-lists-id-members <list_id> --status subscribed | jq '.members[].email_address'
# Create a campaign and get its send checklist
cli-anything-mailchimp --json campaigns create --data '{"type":"regular","settings":{"subject_line":"Hello","from_name":"Me","reply_to":"me@example.com"}}' | jq '.id'
cli-anything-mailchimp --json campaigns list-send-checklist <campaign_id> | jq '.items[] | select(.result == false)'
# Get unsubscribes for a sent campaign
cli-anything-mailchimp --json reports list-unsubscribed <campaign_id> | jq '.unsubscribes[].email_address'
# Add a member to an audience (subscriber hash = MD5 of lowercased email)
cli-anything-mailchimp --json lists create-members <list_id> --data '{"email_address":"user@example.com","status":"subscribed"}'
# Search for a member across all audiences
cli-anything-mailchimp --json search-members list --query "user@example.com" | jq '.exact_matches.members[]'
```
## Interactive REPL
Run `cli-anything-mailchimp` with no arguments to enter the REPL:
```
◆ cli-anything · Mailchimp
v0.1.0
Type help for commands, quit to exit
◆ mailchimp ❯ ping
✓ {"health_status": "Everything's Chimpy!"}
◆ mailchimp ❯ --json lists list
{"lists": [...], "total_items": 3, "_links": [...]}
◆ mailchimp ❯ quit
```
## Notes
- **Subscriber hash**: Mailchimp identifies members by the MD5 hash of their lowercased email. Compute with `python -c "import hashlib; email='email@example.com'; print(hashlib.md5(email.strip().lower().encode()).hexdigest())"`.
- **Body payloads**: All POST/PATCH/PUT commands accept `--data '<json>'`. See [Mailchimp API docs](https://mailchimp.com/developer/marketing/api/) for the schema of each endpoint.
- **Datacenter**: Your API key ends in `-us8`, `-eu2`, etc. The CLI extracts this automatically — include it in `MAILCHIMP_API_KEY`.
- **Rate limits**: The Marketing API rate-limits at 10 concurrent connections and a rolling per-account limit. For bulk operations, use the `batches` group.
@@ -0,0 +1,39 @@
# Test Guide — cli-anything-mailchimp
## Prerequisites
```bash
pip install -e ".[dev]"
# dev extras: pytest, pytest-cov, responses
```
## Unit tests (no API key needed)
```bash
cd mailchimp/agent-harness
pytest cli_anything/mailchimp/tests/test_core.py -v
```
Expected: all collected tests pass. Runtime < 5 seconds.
## Live E2E tests (requires MAILCHIMP_API_KEY)
```bash
export MAILCHIMP_API_KEY=<your-key>-<datacenter>
pytest cli_anything/mailchimp/tests/test_full_e2e.py -v -s
```
Expected: 9 tests. The fixture creates a throwaway audience, runs member and
merge-field operations against it, then deletes it. Runtime ~15–30 seconds.
Cleanup is automatic (the `test_list` fixture deletes the audience in a
`finally` block). If a run is interrupted, delete the `cli-anything-test-*`
list from your Mailchimp account manually.
## Coverage
```bash
pytest cli_anything/mailchimp/tests/test_core.py --cov=cli_anything.mailchimp.core --cov-report=term-missing
```
Target: ≥ 80% line coverage on `core/`.
@@ -0,0 +1,566 @@
"""Unit tests for cli-anything-mailchimp core modules."""
from __future__ import annotations
import json
import sys
import unittest
from unittest.mock import MagicMock, patch
import responses as resp_lib
import requests
# ── client tests ──────────────────────────────────────────────────────
class TestServerPrefix(unittest.TestCase):
def test_extracts_suffix(self):
from cli_anything.mailchimp.core.client import _server_prefix
assert _server_prefix("abc123-us8") == "us8"
assert _server_prefix("xyz-eu2") == "eu2"
def test_missing_suffix_raises(self):
from cli_anything.mailchimp.core.client import _server_prefix
with self.assertRaises(ValueError):
_server_prefix("nodashinkey")
class TestSubscriberHash(unittest.TestCase):
def test_md5_lowercased(self):
from cli_anything.mailchimp.core.client import subscriber_hash
# Known MD5 of "test@example.com"
assert subscriber_hash("test@example.com") == "55502f40dc8b7c769880b10874abc9d0"
# Should normalise to lowercase before hashing
assert subscriber_hash("TEST@EXAMPLE.COM") == subscriber_hash("test@example.com")
def test_strips_whitespace(self):
from cli_anything.mailchimp.core.client import subscriber_hash
assert subscriber_hash(" test@example.com ") == subscriber_hash("test@example.com")
class TestMailchimpClient(unittest.TestCase):
def _make_client(self, key: str = "testkey-us1"):
from cli_anything.mailchimp.core.client import MailchimpClient
return MailchimpClient(api_key=key)
def test_base_url(self):
client = self._make_client("abc-us8")
assert client._base == "https://us8.api.mailchimp.com/3.0"
def test_auth_header(self):
client = self._make_client("abc-us8")
assert client._session.auth == ("anystring", "abc-us8")
def test_missing_key_raises_auth_error(self):
from cli_anything.mailchimp.core.client import MailchimpClient, MailchimpAuthError
import os
orig = os.environ.pop("MAILCHIMP_API_KEY", None)
try:
with self.assertRaises(MailchimpAuthError):
MailchimpClient(api_key=None)
finally:
if orig:
os.environ["MAILCHIMP_API_KEY"] = orig
def test_get_client_exits_on_missing_key(self):
from cli_anything.mailchimp.core.client import get_client
import os
orig = os.environ.pop("MAILCHIMP_API_KEY", None)
try:
with self.assertRaises(SystemExit):
get_client()
finally:
if orig:
os.environ["MAILCHIMP_API_KEY"] = orig
def test_get_client_exits_cleanly_on_key_without_suffix(self):
from cli_anything.mailchimp.core.client import get_client
import os
orig = os.environ.get("MAILCHIMP_API_KEY")
os.environ["MAILCHIMP_API_KEY"] = "invalid"
try:
with self.assertRaises(SystemExit):
get_client()
finally:
if orig is None:
os.environ.pop("MAILCHIMP_API_KEY", None)
else:
os.environ["MAILCHIMP_API_KEY"] = orig
@resp_lib.activate
def test_get_success(self):
from cli_anything.mailchimp.core.client import MailchimpClient
client = MailchimpClient(api_key="key-us1")
resp_lib.add(
resp_lib.GET,
"https://us1.api.mailchimp.com/3.0/ping",
json={"health_status": "Everything's Chimpy!"},
status=200,
)
result = client.get("/ping")
assert result["health_status"] == "Everything's Chimpy!"
@resp_lib.activate
def test_get_error_raises(self):
from cli_anything.mailchimp.core.client import MailchimpClient, MailchimpError
client = MailchimpClient(api_key="key-us1")
resp_lib.add(
resp_lib.GET,
"https://us1.api.mailchimp.com/3.0/lists/bad",
json={"title": "Resource Not Found", "detail": "The requested resource could not be found.", "status": 404},
status=404,
)
with self.assertRaises(MailchimpError) as ctx:
client.get("/lists/bad")
assert ctx.exception.status == 404
assert "Resource Not Found" in ctx.exception.title
@resp_lib.activate
def test_post_success(self):
from cli_anything.mailchimp.core.client import MailchimpClient
client = MailchimpClient(api_key="key-us1")
resp_lib.add(
resp_lib.POST,
"https://us1.api.mailchimp.com/3.0/lists",
json={"id": "abc123", "name": "My List"},
status=200,
)
result = client.post("/lists", json={"name": "My List"})
assert result["id"] == "abc123"
@resp_lib.activate
def test_delete_returns_ok(self):
from cli_anything.mailchimp.core.client import MailchimpClient
client = MailchimpClient(api_key="key-us1")
resp_lib.add(
resp_lib.DELETE,
"https://us1.api.mailchimp.com/3.0/lists/abc123",
status=204,
)
result = client.delete("/lists/abc123")
assert result == {"ok": True}
def test_patch_forwards_query_params(self):
from cli_anything.mailchimp.core.client import MailchimpClient
client = MailchimpClient(api_key="key-us1")
client._session.patch = MagicMock()
client._session.patch.return_value.ok = True
client._session.patch.return_value.json.return_value = {"id": "contact-1"}
result = client.patch(
"/audiences/audience-1/contacts/contact-1",
json={"email_address": "user@example.com"},
params={"data_mode": "sync"},
)
assert result == {"id": "contact-1"}
client._session.patch.assert_called_once_with(
"https://us1.api.mailchimp.com/3.0/audiences/audience-1/contacts/contact-1",
json={"email_address": "user@example.com"},
params={"data_mode": "sync"},
timeout=30,
)
def test_put_forwards_query_params(self):
from cli_anything.mailchimp.core.client import MailchimpClient
client = MailchimpClient(api_key="key-us1")
client._session.put = MagicMock()
client._session.put.return_value.ok = True
client._session.put.return_value.json.return_value = {"id": "member-1"}
result = client.put(
"/lists/list-1/members/hash-1",
json={"email_address": "user@example.com"},
params={"skip_merge_validation": True},
)
assert result == {"id": "member-1"}
client._session.put.assert_called_once_with(
"https://us1.api.mailchimp.com/3.0/lists/list-1/members/hash-1",
json={"email_address": "user@example.com"},
params={"skip_merge_validation": True},
timeout=30,
)
def test_delete_forwards_query_params(self):
from cli_anything.mailchimp.core.client import MailchimpClient
client = MailchimpClient(api_key="key-us1")
client._session.delete = MagicMock()
client._session.delete.return_value.ok = True
result = client.delete("/lists/list-1", params={"force": True})
assert result == {"ok": True}
client._session.delete.assert_called_once_with(
"https://us1.api.mailchimp.com/3.0/lists/list-1",
params={"force": True},
timeout=30,
)
# ── pagination tests ──────────────────────────────────────────────────
class TestPagination(unittest.TestCase):
def _mock_client(self, pages: list[list]) -> MagicMock:
client = MagicMock()
side_effects = [
{"lists": page, "total_items": sum(len(p) for p in pages)}
for page in pages
]
client.get.side_effect = side_effects
return client
def test_single_page(self):
from cli_anything.mailchimp.core.pagination import paginate
client = self._mock_client([[{"id": "1"}, {"id": "2"}]])
items = list(paginate(client, "/lists", "lists", page_size=100))
assert items == [{"id": "1"}, {"id": "2"}]
assert client.get.call_count == 1
def test_multiple_pages(self):
from cli_anything.mailchimp.core.pagination import paginate
page1 = [{"id": str(i)} for i in range(5)]
page2 = [{"id": str(i)} for i in range(5, 8)]
client = self._mock_client([page1, page2])
items = list(paginate(client, "/lists", "lists", page_size=5))
assert len(items) == 8
assert client.get.call_count == 2
def test_empty_result(self):
from cli_anything.mailchimp.core.pagination import paginate
client = self._mock_client([[]])
items = list(paginate(client, "/lists", "lists"))
assert items == []
def test_exact_page_boundary_no_extra_fetch(self):
"""When total_items == page_size, paginator must not fetch a second empty page (I6 fix)."""
from cli_anything.mailchimp.core.pagination import paginate
page = [{"id": str(i)} for i in range(5)]
client = MagicMock()
# First call returns exactly page_size items with total_items matching
client.get.return_value = {"lists": page, "total_items": 5}
items = list(paginate(client, "/lists", "lists", page_size=5))
assert len(items) == 5
assert client.get.call_count == 1 # must not make a second call
def test_collect_returns_total(self):
from cli_anything.mailchimp.core.pagination import collect
client = MagicMock()
client.get.return_value = {"lists": [{"id": "1"}], "total_items": 42}
items, total = collect(client, "/lists", "lists")
assert items == [{"id": "1"}]
assert total == 42
# ── output tests ──────────────────────────────────────────────────────
class TestOutput(unittest.TestCase):
def setUp(self):
import cli_anything.mailchimp.utils.output as out
self._orig = out.USE_JSON
out.USE_JSON = False
def tearDown(self):
import cli_anything.mailchimp.utils.output as out
out.USE_JSON = self._orig
def test_json_mode_outputs_json(self):
import cli_anything.mailchimp.utils.output as out
import io
out.USE_JSON = True
captured = io.StringIO()
with patch("builtins.print", lambda *a, **kw: captured.write(str(a[0]) + "\n")):
out._out({"key": "value"})
parsed = json.loads(captured.getvalue())
assert parsed["key"] == "value"
# ── smoke import tests ────────────────────────────────────────────────
class TestGeneratedCommandsImport(unittest.TestCase):
"""Verify every generated command module imports cleanly and registers commands.
This catches codegen regressions like C1 (builtin shadowing) which would
cause Click to fail silently or raise at import time.
"""
def test_all_groups_importable(self):
from cli_anything.mailchimp.commands import ALL_GROUPS
assert len(ALL_GROUPS) == 30, f"Expected 30 groups, got {len(ALL_GROUPS)}"
for group in ALL_GROUPS:
assert group.name is not None
assert len(group.commands) > 0, f"Group {group.name!r} has no commands"
def test_generator_preserves_complete_first_line_help(self):
from cli_anything.mailchimp._codegen.generate import _click_help_text
description = (
"Used for [pagination](https://mailchimp.com/developer/marketing/docs/fundamentals/#pagination) "
"with enough context to exceed eighty characters.\nSecond line omitted."
)
help_text = _click_help_text(description)
assert help_text.endswith("eighty characters.")
assert "\n" not in help_text
def test_no_builtin_shadowing_in_function_names(self):
"""Ensure generated functions are prefixed with _cmd_ (not builtins)."""
import glob
import ast
import builtins as _builtins
builtin_names = set(dir(_builtins))
issues = []
for path in glob.glob("cli_anything/mailchimp/commands/*.py"):
tree = ast.parse(open(path).read())
for node in ast.walk(tree):
if isinstance(node, ast.FunctionDef):
if node.name in builtin_names:
issues.append(f"{path}: def {node.name}()")
assert not issues, "Builtin shadowing in generated code:\n" + "\n".join(issues)
def test_all_commands_have_extra_params(self):
"""Every generated command must accept --extra-params (I3 fix)."""
from cli_anything.mailchimp.commands import ALL_GROUPS
import click
missing = []
for group in ALL_GROUPS:
for cmd_name, cmd in group.commands.items():
if isinstance(cmd, click.Group):
continue
param_names = [p.name for p in cmd.params]
if "extra_params" not in param_names:
missing.append(f"{group.name} {cmd_name}")
assert not missing, "--extra-params missing on:\n" + "\n".join(missing)
def test_generated_patch_command_forwards_query_params(self):
from click.testing import CliRunner
from cli_anything.mailchimp.commands.audiences import audiences_group
client = MagicMock()
client.patch.return_value = {"id": "contact-1"}
with patch("cli_anything.mailchimp.core.client.get_client", return_value=client):
result = CliRunner().invoke(
audiences_group,
[
"update",
"audience-1",
"contact-1",
"--data",
'{"email_address":"user@example.com"}',
"--data-mode",
"sync",
],
)
assert result.exit_code == 0, result.output
client.patch.assert_called_once_with(
"/audiences/audience-1/contacts/contact-1",
json={"email_address": "user@example.com"},
params={"data_mode": "sync"},
)
def test_generated_put_command_forwards_query_params(self):
from click.testing import CliRunner
from cli_anything.mailchimp.commands.lists import lists_group
client = MagicMock()
client.put.return_value = {"id": "member-1"}
with patch("cli_anything.mailchimp.core.client.get_client", return_value=client):
result = CliRunner().invoke(
lists_group,
[
"update-3",
"list-1",
"hash-1",
"--data",
'{"email_address":"user@example.com"}',
"--skip-merge-validation",
"true",
],
)
assert result.exit_code == 0, result.output
client.put.assert_called_once_with(
"/lists/list-1/members/hash-1",
json={"email_address": "user@example.com"},
params={"skip_merge_validation": True},
)
def test_generated_keyword_query_option_uses_safe_callback_name(self):
from click.testing import CliRunner
from cli_anything.mailchimp.commands.templates import templates_group
client = MagicMock()
client.get.return_value = {"templates": []}
with patch("cli_anything.mailchimp.core.client.get_client", return_value=client):
result = CliRunner().invoke(templates_group, ["list", "--type", "user"])
assert result.exit_code == 0, result.output
client.get.assert_called_once_with("/templates", params={"type": "user"})
def test_campaign_shortcut_aliases_match_generated_commands(self):
from click.testing import CliRunner
from cli_anything.mailchimp.commands.campaigns import campaigns_group
client = MagicMock()
client.get.return_value = {"items": []}
with patch("cli_anything.mailchimp.core.client.get_client", return_value=client):
content = CliRunner().invoke(campaigns_group, ["list-content", "campaign-1"])
checklist = CliRunner().invoke(campaigns_group, ["list-send-checklist", "campaign-1"])
assert content.exit_code == 0, content.output
assert checklist.exit_code == 0, checklist.output
client.get.assert_any_call("/campaigns/campaign-1/content", params=None)
client.get.assert_any_call("/campaigns/campaign-1/send-checklist", params=None)
def test_report_shortcut_aliases_match_generated_commands(self):
from click.testing import CliRunner
from cli_anything.mailchimp.commands.reports import reports_group
client = MagicMock()
client.get.return_value = {"items": []}
shortcuts = {
"list-email-activity": "/reports/campaign-1/email-activity",
"list-click-details": "/reports/campaign-1/click-details",
"list-open-details": "/reports/campaign-1/open-details",
"list-unsubscribed": "/reports/campaign-1/unsubscribed",
"list-locations": "/reports/campaign-1/locations",
"list-domain-performance": "/reports/campaign-1/domain-performance",
}
with patch("cli_anything.mailchimp.core.client.get_client", return_value=client):
for command in shortcuts:
result = CliRunner().invoke(reports_group, [command, "campaign-1"])
assert result.exit_code == 0, result.output
for path in shortcuts.values():
client.get.assert_any_call(path, params=None)
def test_automation_and_segment_shortcut_aliases_match_generated_commands(self):
from click.testing import CliRunner
from cli_anything.mailchimp.commands.automations import automations_group
from cli_anything.mailchimp.commands.lists import lists_group
client = MagicMock()
client.get.return_value = {"items": []}
with patch("cli_anything.mailchimp.core.client.get_client", return_value=client):
emails = CliRunner().invoke(automations_group, ["list-emails", "workflow-1"])
segments = CliRunner().invoke(lists_group, ["list-lists-id-segments", "list-1"])
assert emails.exit_code == 0, emails.output
assert segments.exit_code == 0, segments.output
client.get.assert_any_call("/automations/workflow-1/emails", params=None)
client.get.assert_any_call("/lists/list-1/segments", params=None)
def test_ping_group_invokes_health_check_without_list_subcommand(self):
from click.testing import CliRunner
from cli_anything.mailchimp.mailchimp_cli import cli
client = MagicMock()
client.get.return_value = {"health_status": "Everything's Chimpy!"}
with patch("cli_anything.mailchimp.core.client.get_client", return_value=client):
result = CliRunner().invoke(cli, ["ping"])
assert result.exit_code == 0, result.output
client.get.assert_called_once_with("/ping", params=None)
def test_create_members_alias_matches_generated_command(self):
from click.testing import CliRunner
from cli_anything.mailchimp.commands.lists import lists_group
client = MagicMock()
client.post.return_value = {"id": "member-1"}
with patch("cli_anything.mailchimp.core.client.get_client", return_value=client):
result = CliRunner().invoke(
lists_group,
[
"create-members",
"list-1",
"--data",
'{"email_address":"user@example.com","status":"subscribed"}',
"--skip-merge-validation",
"true",
],
)
assert result.exit_code == 0, result.output
client.post.assert_called_once_with(
"/lists/list-1/members",
json={"email_address": "user@example.com", "status": "subscribed"},
params={"skip_merge_validation": True},
)
def test_invalid_data_json_reports_click_error(self):
from click.testing import CliRunner
from cli_anything.mailchimp.commands.lists import lists_group
result = CliRunner().invoke(lists_group, ["create", "--data", "{bad"])
assert result.exit_code == 2
assert "Invalid value for --data" in result.output
assert "valid JSON" in result.output
assert "Traceback" not in result.output
def test_extra_params_must_be_json_object(self):
from click.testing import CliRunner
from cli_anything.mailchimp.commands.lists import lists_group
result = CliRunner().invoke(lists_group, ["list", "--extra-params", "[]"])
assert result.exit_code == 2
assert "Invalid value for --extra-params" in result.output
assert "JSON object" in result.output
assert "Traceback" not in result.output
def test_cli_without_subcommand_starts_repl_through_click(self):
from click.testing import CliRunner
import cli_anything.mailchimp.mailchimp_cli as mailchimp_cli
with patch.object(mailchimp_cli, "_start_repl") as start_repl:
result = CliRunner().invoke(mailchimp_cli.cli, [])
assert result.exit_code == 0, result.output
start_repl.assert_called_once_with()
if __name__ == "__main__":
unittest.main()
@@ -0,0 +1,127 @@
"""End-to-end tests against the live Mailchimp API.
Skipped unless MAILCHIMP_API_KEY is set. Creates a throwaway audience,
exercises member and merge-field operations, then cleans up.
Run:
MAILCHIMP_API_KEY=<key>-<dc> pytest tests/test_full_e2e.py -v -s
"""
from __future__ import annotations
import os
import uuid
import pytest
pytestmark = pytest.mark.skipif(
not os.environ.get("MAILCHIMP_API_KEY"),
reason="MAILCHIMP_API_KEY not set — skipping live API tests",
)
TEST_LIST_NAME = f"cli-anything-test-{uuid.uuid4().hex[:8]}"
TEST_EMAIL = f"cli-test-{uuid.uuid4().hex[:8]}@example.com"
@pytest.fixture(scope="module")
def client():
from cli_anything.mailchimp.core.client import MailchimpClient
return MailchimpClient()
@pytest.fixture(scope="module")
def test_list(client):
"""Create a throwaway Mailchimp audience, yield its ID, then delete it."""
body = {
"name": TEST_LIST_NAME,
"contact": {
"company": "CLI-Anything Test",
"address1": "123 Test St",
"city": "Testville",
"state": "CA",
"zip": "90210",
"country": "US",
},
"permission_reminder": "test permission reminder",
"email_type_option": False,
"campaign_defaults": {
"from_name": "CLI Test",
"from_email": "test@example.com",
"subject": "Test",
"language": "en",
},
}
result = client.post("/lists", json=body)
list_id = result["id"]
yield list_id
# Cleanup — delete the test list
try:
client.delete(f"/lists/{list_id}")
except Exception:
pass
class TestPing:
def test_ping(self, client):
result = client.get("/ping")
assert "health_status" in result
class TestLists:
def test_get_lists(self, client):
result = client.get("/lists", params={"count": 1})
assert "lists" in result
assert isinstance(result["lists"], list)
def test_create_and_get_list(self, client, test_list):
result = client.get(f"/lists/{test_list}")
assert result["id"] == test_list
assert result["name"] == TEST_LIST_NAME
def test_update_list(self, client, test_list):
new_name = TEST_LIST_NAME + "-updated"
result = client.patch(f"/lists/{test_list}", json={"name": new_name})
assert result["name"] == new_name
# Restore
client.patch(f"/lists/{test_list}", json={"name": TEST_LIST_NAME})
class TestMembers:
def test_add_member(self, client, test_list):
body = {
"email_address": TEST_EMAIL,
"status": "subscribed",
}
result = client.post(f"/lists/{test_list}/members", json=body)
assert result["email_address"] == TEST_EMAIL
assert result["status"] == "subscribed"
def test_get_member(self, client, test_list):
from cli_anything.mailchimp.core.client import subscriber_hash
h = subscriber_hash(TEST_EMAIL)
result = client.get(f"/lists/{test_list}/members/{h}")
assert result["email_address"] == TEST_EMAIL
def test_list_members(self, client, test_list):
result = client.get(f"/lists/{test_list}/members", params={"count": 10})
emails = [m["email_address"] for m in result.get("members", [])]
assert TEST_EMAIL in emails
def test_archive_member(self, client, test_list):
from cli_anything.mailchimp.core.client import subscriber_hash
h = subscriber_hash(TEST_EMAIL)
result = client.delete(f"/lists/{test_list}/members/{h}")
assert result == {"ok": True}
class TestMergeFields:
def test_create_and_list_merge_fields(self, client, test_list):
body = {"tag": "TESTFIELD", "name": "Test Field", "type": "text"}
result = client.post(f"/lists/{test_list}/merge-fields", json=body)
assert result["tag"] == "TESTFIELD"
fields = client.get(f"/lists/{test_list}/merge-fields")
tags = [f["tag"] for f in fields.get("merge_fields", [])]
assert "TESTFIELD" in tags
@@ -0,0 +1,86 @@
"""JSON / human output helpers for cli-anything-mailchimp."""
from __future__ import annotations
import json
import sys
from typing import Any
# Module-level flag set by the root CLI group's --json option.
USE_JSON: bool = False
def _out(data: Any) -> None:
"""Print data as JSON (when --json) or human-readable text."""
if USE_JSON:
print(json.dumps(data, indent=2, default=str))
elif isinstance(data, dict):
_print_dict(data)
elif isinstance(data, list):
_print_list(data)
else:
print(data)
def _out_ok(message: str, data: dict | None = None) -> None:
"""Print a success/mutation result.
JSON shape: {"ok": true, "message": "<msg>"} or {"ok": true, "message": "...", "data": {...}}
"""
if USE_JSON:
payload: dict = {"ok": True, "message": message}
if data:
payload["data"] = data
print(json.dumps(payload, indent=2, default=str))
else:
print(f" \033[38;5;78m✓\033[0m {message}")
if data:
_print_dict(data)
def _out_err(status: int, title: str, detail: str, raw: dict | None = None) -> None:
"""Print an error and exit non-zero."""
if USE_JSON:
print(
json.dumps(
{"ok": False, "message": f"{title}: {detail}", "data": raw or {}},
indent=2,
),
file=sys.stderr,
)
else:
print(f" \033[38;5;196m✗\033[0m HTTP {status}: {title}", file=sys.stderr)
if detail:
print(f" {detail}", file=sys.stderr)
sys.exit(1)
# ── Formatting helpers ─────────────────────────────────────────────────
_GRAY = "\033[38;5;245m"
_WHITE = "\033[97m"
_CYAN = "\033[38;5;80m"
_RESET = "\033[0m"
def _print_dict(d: dict, indent: int = 2) -> None:
prefix = " " * indent
for k, v in d.items():
key = f"{_GRAY}{k}:{_RESET}"
if isinstance(v, dict):
print(f"{prefix}{key}")
_print_dict(v, indent + 2)
elif isinstance(v, list):
print(f"{prefix}{key} [{len(v)} items]")
else:
print(f"{prefix}{key} {_WHITE}{v}{_RESET}")
def _print_list(items: list, indent: int = 2) -> None:
for item in items:
if isinstance(item, dict):
_print_dict(item, indent)
print()
else:
print(" " * indent + str(item))
@@ -0,0 +1,567 @@
"""cli-anything REPL Skin — Unified terminal interface for all CLI harnesses.
Copy this file into your CLI package at:
cli_anything/<software>/utils/repl_skin.py
Usage:
from cli_anything.<software>.utils.repl_skin import ReplSkin
skin = ReplSkin("shotcut", version="1.0.0")
skin.print_banner() # auto-detects repo-root or packaged SKILL.md
prompt_text = skin.prompt(project_name="my_video.mlt", modified=True)
skin.success("Project saved")
skin.error("File not found")
skin.warning("Unsaved changes")
skin.info("Processing 24 clips...")
skin.status("Track 1", "3 clips, 00:02:30")
skin.table(headers, rows)
skin.print_goodbye()
"""
import os
import sys
from pathlib import Path
# ── ANSI color codes (no external deps for core styling) ──────────────
_RESET = "\033[0m"
_BOLD = "\033[1m"
_DIM = "\033[2m"
_ITALIC = "\033[3m"
_UNDERLINE = "\033[4m"
# Brand colors
_CYAN = "\033[38;5;80m" # cli-anything brand cyan
_CYAN_BG = "\033[48;5;80m"
_WHITE = "\033[97m"
_GRAY = "\033[38;5;245m"
_DARK_GRAY = "\033[38;5;240m"
_LIGHT_GRAY = "\033[38;5;250m"
# Software accent colors — each software gets a unique accent
_ACCENT_COLORS = {
"gimp": "\033[38;5;214m", # warm orange
"blender": "\033[38;5;208m", # deep orange
"inkscape": "\033[38;5;39m", # bright blue
"audacity": "\033[38;5;33m", # navy blue
"libreoffice": "\033[38;5;40m", # green
"obs_studio": "\033[38;5;55m", # purple
"kdenlive": "\033[38;5;69m", # slate blue
"shotcut": "\033[38;5;35m", # teal green
}
_DEFAULT_ACCENT = "\033[38;5;75m" # default sky blue
# Status colors
_GREEN = "\033[38;5;78m"
_YELLOW = "\033[38;5;220m"
_RED = "\033[38;5;196m"
_BLUE = "\033[38;5;75m"
_MAGENTA = "\033[38;5;176m"
_SKILL_SOURCE_REPO = os.environ.get("CLI_ANYTHING_SKILL_REPO", "HKUDS/CLI-Anything")
# ── Brand icon ────────────────────────────────────────────────────────
# The cli-anything icon: a small colored diamond/chevron mark
_ICON = f"{_CYAN}{_BOLD}◆{_RESET}"
_ICON_SMALL = f"{_CYAN}▸{_RESET}"
# ── Box drawing characters ────────────────────────────────────────────
_H_LINE = "─"
_V_LINE = "│"
_TL = "╭"
_TR = "╮"
_BL = "╰"
_BR = "╯"
_T_DOWN = "┬"
_T_UP = "┴"
_T_RIGHT = "├"
_T_LEFT = "┤"
_CROSS = "┼"
def _strip_ansi(text: str) -> str:
"""Remove ANSI escape codes for length calculation."""
import re
return re.sub(r"\033\[[^m]*m", "", text)
def _visible_len(text: str) -> int:
"""Get visible length of text (excluding ANSI codes)."""
return len(_strip_ansi(text))
def _display_home_path(path: str) -> str:
"""Display a path relative to the home directory when possible."""
expanded = Path(path).expanduser().resolve()
home = Path.home().resolve()
try:
relative = expanded.relative_to(home)
return f"~/{relative.as_posix()}"
except ValueError:
return str(expanded)
class ReplSkin:
"""Unified REPL skin for cli-anything CLIs.
Provides consistent branding, prompts, and message formatting
across all CLI harnesses built with the cli-anything methodology.
"""
def __init__(self, software: str, version: str = "1.0.0",
history_file: str | None = None, skill_path: str | None = None):
"""Initialize the REPL skin.
Args:
software: Software name (e.g., "gimp", "shotcut", "blender").
version: CLI version string.
history_file: Path for persistent command history.
Defaults to ~/.cli-anything-<software>/history
skill_path: Path to the SKILL.md file for agent discovery.
Auto-detected from the repo-root skills/ tree when present,
otherwise from the package's skills/ directory.
Displayed in banner for AI agents to know where to read skill info.
"""
self.software = software.lower().replace("-", "_")
self.display_name = software.replace("_", " ").title()
self.version = version
software_aliases = {"iterm2_ctl": "iterm2"}
self.skill_slug = software_aliases.get(self.software, self.software).replace("_", "-")
self.skill_id = f"cli-anything-{self.skill_slug}"
self.skill_install_cmd = (
f"npx skills add {_SKILL_SOURCE_REPO} --skill {self.skill_id} -g -y"
)
global_skill_root = Path(
os.environ.get("CLI_ANYTHING_GLOBAL_SKILLS_DIR", str(Path.home() / ".agents" / "skills"))
).expanduser()
self.global_skill_path = str(global_skill_root / self.skill_id / "SKILL.md")
# Prefer repo-root canonical skills/<skill-id>/SKILL.md when running
# inside the CLI-Anything monorepo. Fall back to the packaged
# cli_anything/<software>/skills/SKILL.md for installed harnesses.
if skill_path is None:
package_skill = Path(__file__).resolve().parent.parent / "skills" / "SKILL.md"
repo_skill = None
for parent in Path(__file__).resolve().parents:
candidate = parent / "skills" / self.skill_id / "SKILL.md"
if candidate.is_file():
repo_skill = candidate
break
if repo_skill and repo_skill.is_file():
skill_path = str(repo_skill)
elif package_skill.is_file():
skill_path = str(package_skill)
self.skill_path = skill_path
self.accent = _ACCENT_COLORS.get(self.software, _DEFAULT_ACCENT)
# History file
if history_file is None:
hist_dir = Path.home() / f".cli-anything-{self.software}"
hist_dir.mkdir(parents=True, exist_ok=True)
self.history_file = str(hist_dir / "history")
else:
self.history_file = history_file
# Detect terminal capabilities
self._color = self._detect_color_support()
def _detect_color_support(self) -> bool:
"""Check if terminal supports color."""
if os.environ.get("NO_COLOR"):
return False
if os.environ.get("CLI_ANYTHING_NO_COLOR"):
return False
if not hasattr(sys.stdout, "isatty"):
return False
return sys.stdout.isatty()
def _c(self, code: str, text: str) -> str:
"""Apply color code if colors are supported."""
if not self._color:
return text
return f"{code}{text}{_RESET}"
# ── Banner ────────────────────────────────────────────────────────
def print_banner(self):
"""Print the startup banner with branding."""
import textwrap
inner = 72
def _box_line(content: str) -> str:
"""Wrap content in box drawing, padding to inner width."""
pad = inner - _visible_len(content)
vl = self._c(_DARK_GRAY, _V_LINE)
return f"{vl}{content}{' ' * max(0, pad)}{vl}"
def _meta_lines(label: str, value: str) -> list[str]:
"""Wrap a metadata line for the banner box."""
icon = self._c(_MAGENTA, "◇")
label_text = self._c(_DARK_GRAY, label)
prefix = f" {icon} {label_text} "
available = max(12, inner - _visible_len(prefix))
wrapped = textwrap.wrap(
value,
width=available,
break_long_words=True,
break_on_hyphens=False,
) or [""]
lines = [f"{prefix}{self._c(_LIGHT_GRAY, wrapped[0])}"]
continuation_prefix = " " * _visible_len(prefix)
for chunk in wrapped[1:]:
lines.append(f"{continuation_prefix}{self._c(_LIGHT_GRAY, chunk)}")
return lines
top = self._c(_DARK_GRAY, f"{_TL}{_H_LINE * inner}{_TR}")
bot = self._c(_DARK_GRAY, f"{_BL}{_H_LINE * inner}{_BR}")
# Title: ◆ cli-anything · Shotcut
icon = self._c(_CYAN + _BOLD, "◆")
brand = self._c(_CYAN + _BOLD, "cli-anything")
dot = self._c(_DARK_GRAY, "·")
name = self._c(self.accent + _BOLD, self.display_name)
title = f" {icon} {brand} {dot} {name}"
ver = f" {self._c(_DARK_GRAY, f' v{self.version}')}"
tip = f" {self._c(_DARK_GRAY, ' Type help for commands, quit to exit')}"
empty = ""
meta_lines: list[str] = []
meta_lines.extend(_meta_lines("Install:", self.skill_install_cmd))
meta_lines.extend(_meta_lines("Global skill:", _display_home_path(self.global_skill_path)))
print(top)
print(_box_line(title))
print(_box_line(ver))
for line in meta_lines:
print(_box_line(line))
print(_box_line(empty))
print(_box_line(tip))
print(bot)
print()
# ── Prompt ────────────────────────────────────────────────────────
def prompt(self, project_name: str = "", modified: bool = False,
context: str = "") -> str:
"""Build a styled prompt string for prompt_toolkit or input().
Args:
project_name: Current project name (empty if none open).
modified: Whether the project has unsaved changes.
context: Optional extra context to show in prompt.
Returns:
Formatted prompt string.
"""
parts = []
# Icon
if self._color:
parts.append(f"{_CYAN}◆{_RESET} ")
else:
parts.append("> ")
# Software name
parts.append(self._c(self.accent + _BOLD, self.software))
# Project context
if project_name or context:
ctx = context or project_name
mod = "*" if modified else ""
parts.append(f" {self._c(_DARK_GRAY, '[')}")
parts.append(self._c(_LIGHT_GRAY, f"{ctx}{mod}"))
parts.append(self._c(_DARK_GRAY, ']'))
parts.append(self._c(_GRAY, " ❯ "))
return "".join(parts)
def prompt_tokens(self, project_name: str = "", modified: bool = False,
context: str = ""):
"""Build prompt_toolkit formatted text tokens for the prompt.
Use with prompt_toolkit's FormattedText for proper ANSI handling.
Returns:
list of (style, text) tuples for prompt_toolkit.
"""
accent_hex = _ANSI_256_TO_HEX.get(self.accent, "#5fafff")
tokens = []
tokens.append(("class:icon", "◆ "))
tokens.append(("class:software", self.software))
if project_name or context:
ctx = context or project_name
mod = "*" if modified else ""
tokens.append(("class:bracket", " ["))
tokens.append(("class:context", f"{ctx}{mod}"))
tokens.append(("class:bracket", "]"))
tokens.append(("class:arrow", " ❯ "))
return tokens
def get_prompt_style(self):
"""Get a prompt_toolkit Style object matching the skin.
Returns:
prompt_toolkit.styles.Style
"""
try:
from prompt_toolkit.styles import Style
except ImportError:
return None
accent_hex = _ANSI_256_TO_HEX.get(self.accent, "#5fafff")
return Style.from_dict({
"icon": "#5fdfdf bold", # cyan brand color
"software": f"{accent_hex} bold",
"bracket": "#585858",
"context": "#bcbcbc",
"arrow": "#808080",
# Completion menu
"completion-menu.completion": "bg:#303030 #bcbcbc",
"completion-menu.completion.current": f"bg:{accent_hex} #000000",
"completion-menu.meta.completion": "bg:#303030 #808080",
"completion-menu.meta.completion.current": f"bg:{accent_hex} #000000",
# Auto-suggest
"auto-suggest": "#585858",
# Bottom toolbar
"bottom-toolbar": "bg:#1c1c1c #808080",
"bottom-toolbar.text": "#808080",
})
# ── Messages ──────────────────────────────────────────────────────
def success(self, message: str):
"""Print a success message with green checkmark."""
icon = self._c(_GREEN + _BOLD, "✓")
print(f" {icon} {self._c(_GREEN, message)}")
def error(self, message: str):
"""Print an error message with red cross."""
icon = self._c(_RED + _BOLD, "✗")
print(f" {icon} {self._c(_RED, message)}", file=sys.stderr)
def warning(self, message: str):
"""Print a warning message with yellow triangle."""
icon = self._c(_YELLOW + _BOLD, "⚠")
print(f" {icon} {self._c(_YELLOW, message)}")
def info(self, message: str):
"""Print an info message with blue dot."""
icon = self._c(_BLUE, "●")
print(f" {icon} {self._c(_LIGHT_GRAY, message)}")
def hint(self, message: str):
"""Print a subtle hint message."""
print(f" {self._c(_DARK_GRAY, message)}")
def section(self, title: str):
"""Print a section header."""
print()
print(f" {self._c(self.accent + _BOLD, title)}")
print(f" {self._c(_DARK_GRAY, _H_LINE * len(title))}")
# ── Status display ────────────────────────────────────────────────
def status(self, label: str, value: str):
"""Print a key-value status line."""
lbl = self._c(_GRAY, f" {label}:")
val = self._c(_WHITE, f" {value}")
print(f"{lbl}{val}")
def status_block(self, items: dict[str, str], title: str = ""):
"""Print a block of status key-value pairs.
Args:
items: Dict of label -> value pairs.
title: Optional title for the block.
"""
if title:
self.section(title)
max_key = max(len(k) for k in items) if items else 0
for label, value in items.items():
lbl = self._c(_GRAY, f" {label:<{max_key}}")
val = self._c(_WHITE, f" {value}")
print(f"{lbl}{val}")
def progress(self, current: int, total: int, label: str = ""):
"""Print a simple progress indicator.
Args:
current: Current step number.
total: Total number of steps.
label: Optional label for the progress.
"""
pct = int(current / total * 100) if total > 0 else 0
bar_width = 20
filled = int(bar_width * current / total) if total > 0 else 0
bar = "█" * filled + "░" * (bar_width - filled)
text = f" {self._c(_CYAN, bar)} {self._c(_GRAY, f'{pct:3d}%')}"
if label:
text += f" {self._c(_LIGHT_GRAY, label)}"
print(text)
# ── Table display ─────────────────────────────────────────────────
def table(self, headers: list[str], rows: list[list[str]],
max_col_width: int = 40):
"""Print a formatted table with box-drawing characters.
Args:
headers: Column header strings.
rows: List of rows, each a list of cell strings.
max_col_width: Maximum column width before truncation.
"""
if not headers:
return
# Calculate column widths
col_widths = [min(len(h), max_col_width) for h in headers]
for row in rows:
for i, cell in enumerate(row):
if i < len(col_widths):
col_widths[i] = min(
max(col_widths[i], len(str(cell))), max_col_width
)
def pad(text: str, width: int) -> str:
t = str(text)[:width]
return t + " " * (width - len(t))
# Header
header_cells = [
self._c(_CYAN + _BOLD, pad(h, col_widths[i]))
for i, h in enumerate(headers)
]
sep = self._c(_DARK_GRAY, f" {_V_LINE} ")
header_line = f" {sep.join(header_cells)}"
print(header_line)
# Separator
sep_parts = [self._c(_DARK_GRAY, _H_LINE * w) for w in col_widths]
sep_line = self._c(_DARK_GRAY, f" {'───'.join([_H_LINE * w for w in col_widths])}")
print(sep_line)
# Rows
for row in rows:
cells = []
for i, cell in enumerate(row):
if i < len(col_widths):
cells.append(self._c(_LIGHT_GRAY, pad(str(cell), col_widths[i])))
row_sep = self._c(_DARK_GRAY, f" {_V_LINE} ")
print(f" {row_sep.join(cells)}")
# ── Help display ──────────────────────────────────────────────────
def help(self, commands: dict[str, str]):
"""Print a formatted help listing.
Args:
commands: Dict of command -> description pairs.
"""
self.section("Commands")
max_cmd = max(len(c) for c in commands) if commands else 0
for cmd, desc in commands.items():
cmd_styled = self._c(self.accent, f" {cmd:<{max_cmd}}")
desc_styled = self._c(_GRAY, f" {desc}")
print(f"{cmd_styled}{desc_styled}")
print()
# ── Goodbye ───────────────────────────────────────────────────────
def print_goodbye(self):
"""Print a styled goodbye message."""
print(f"\n {_ICON_SMALL} {self._c(_GRAY, 'Goodbye!')}\n")
# ── Prompt toolkit session factory ────────────────────────────────
def create_prompt_session(self):
"""Create a prompt_toolkit PromptSession with skin styling.
Returns:
A configured PromptSession, or None if prompt_toolkit unavailable.
"""
try:
from prompt_toolkit import PromptSession
from prompt_toolkit.history import FileHistory
from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
from prompt_toolkit.formatted_text import FormattedText
style = self.get_prompt_style()
session = PromptSession(
history=FileHistory(self.history_file),
auto_suggest=AutoSuggestFromHistory(),
style=style,
enable_history_search=True,
)
return session
except ImportError:
return None
def get_input(self, pt_session, project_name: str = "",
modified: bool = False, context: str = "") -> str:
"""Get input from user using prompt_toolkit or fallback.
Args:
pt_session: A prompt_toolkit PromptSession (or None).
project_name: Current project name.
modified: Whether project has unsaved changes.
context: Optional context string.
Returns:
User input string (stripped).
"""
if pt_session is not None:
from prompt_toolkit.formatted_text import FormattedText
tokens = self.prompt_tokens(project_name, modified, context)
return pt_session.prompt(FormattedText(tokens)).strip()
else:
raw_prompt = self.prompt(project_name, modified, context)
return input(raw_prompt).strip()
# ── Toolbar builder ───────────────────────────────────────────────
def bottom_toolbar(self, items: dict[str, str]):
"""Create a bottom toolbar callback for prompt_toolkit.
Args:
items: Dict of label -> value pairs to show in toolbar.
Returns:
A callable that returns FormattedText for the toolbar.
"""
def toolbar():
from prompt_toolkit.formatted_text import FormattedText
parts = []
for i, (k, v) in enumerate(items.items()):
if i > 0:
parts.append(("class:bottom-toolbar.text", " │ "))
parts.append(("class:bottom-toolbar.text", f" {k}: "))
parts.append(("class:bottom-toolbar", v))
return FormattedText(parts)
return toolbar
# ── ANSI 256-color to hex mapping (for prompt_toolkit styles) ─────────
_ANSI_256_TO_HEX = {
"\033[38;5;33m": "#0087ff", # audacity navy blue
"\033[38;5;35m": "#00af5f", # shotcut teal
"\033[38;5;39m": "#00afff", # inkscape bright blue
"\033[38;5;40m": "#00d700", # libreoffice green
"\033[38;5;55m": "#5f00af", # obs purple
"\033[38;5;69m": "#5f87ff", # kdenlive slate blue
"\033[38;5;75m": "#5fafff", # default sky blue
"\033[38;5;80m": "#5fd7d7", # brand cyan
"\033[38;5;208m": "#ff8700", # blender deep orange
"\033[38;5;214m": "#ffaf00", # gimp warm orange
}
+38
View File
@@ -0,0 +1,38 @@
import os
from setuptools import setup, find_namespace_packages
_here = os.path.abspath(os.path.dirname(__file__))
with open(os.path.join(_here, "cli_anything/mailchimp/README.md"), encoding="utf-8") as fh:
long_description = fh.read()
setup(
name="cli-anything-mailchimp",
version="0.1.0",
description="cli-anything harness for the Mailchimp Marketing API v3.0",
long_description=long_description,
long_description_content_type="text/markdown",
author="cli-anything contributors",
license="Apache-2.0",
python_requires=">=3.10",
packages=find_namespace_packages(include=["cli_anything.*"]),
package_data={"cli_anything.mailchimp": ["skills/SKILL.md", "README.md"]},
install_requires=[
"click>=8.0",
"requests>=2.28",
"prompt-toolkit>=3.0",
],
extras_require={
"dev": ["pytest>=7.0", "pytest-cov", "responses>=0.23"],
},
entry_points={
"console_scripts": [
"cli-anything-mailchimp=cli_anything.mailchimp.mailchimp_cli:main",
],
},
classifiers=[
"Programming Language :: Python :: 3",
"License :: OSI Approved :: Apache Software License",
"Operating System :: OS Independent",
],
)
+19
View File
@@ -1086,6 +1086,25 @@
"url": "https://github.com/collectivewinca"
}
]
},
{
"name": "mailchimp",
"display_name": "Mailchimp",
"version": "0.1.0",
"description": "Mailchimp Marketing API v3.0 — manage audiences, campaigns, reports, automations, ecommerce, templates, and more from an agent-native CLI",
"requires": "MAILCHIMP_API_KEY",
"homepage": "https://mailchimp.com/developer/marketing/",
"source_url": null,
"install_cmd": "pip install git+https://github.com/HKUDS/CLI-Anything.git#subdirectory=mailchimp/agent-harness",
"entry_point": "cli-anything-mailchimp",
"skill_md": "skills/cli-anything-mailchimp/SKILL.md",
"category": "web",
"contributors": [
{
"name": "iGodly",
"url": "https://github.com/iGodly"
}
]
}
]
}
+228
View File
@@ -0,0 +1,228 @@
---
name: cli-anything-mailchimp
description: CLI harness for the Mailchimp Marketing API v3.0 — 303 commands across 30 resource groups (lists, campaigns, reports, automations, ecommerce, templates, and more). Supports JSON output and interactive REPL mode.
---
# cli-anything-mailchimp
## Identity
An agent-native CLI for the [Mailchimp Marketing API v3.0](https://mailchimp.com/developer/marketing/docs/fundamentals/), built on the [CLI-Anything](https://github.com/HKUDS/CLI-Anything) framework.
## What This CLI Does
- Manage **audiences (lists)** — create, update, delete lists; add/update/archive members; manage merge fields, segments, tags, and webhooks.
- Run **campaigns** — create, schedule, send, pause, replicate, and analyse email campaigns.
- Read **reports** — open rates, click rates, bounce stats, unsubscribes, email activity, geographic data.
- Control **automations** — create and manage automated email workflows.
- Manage **e-commerce** — stores, orders, customers, products, carts, and promo codes.
- Use **templates, file manager, landing pages, SMS campaigns, surveys**, and all other Marketing API resources.
## Prerequisites
- Python 3.10+
- `MAILCHIMP_API_KEY` environment variable set to your API key (including datacenter suffix, e.g. `abc123-us8`)
## Installation
```bash
# From the CLI-Anything repo (once merged):
pip install git+https://github.com/HKUDS/CLI-Anything.git#subdirectory=mailchimp/agent-harness
# During development:
cd mailchimp/agent-harness && pip install -e .
```
## Command Reference
### Root
| Command | Description |
|---|---|
| `cli-anything-mailchimp ping` | Health check — confirms API connectivity |
| `cli-anything-mailchimp root list` | Get account info |
| `cli-anything-mailchimp --json <cmd>` | Output any command as JSON |
| `cli-anything-mailchimp` | Launch interactive REPL |
### Lists (Audiences)
| Command | Description |
|---|---|
| `lists list` | List all audiences |
| `lists get <LIST_ID>` | Get audience info |
| `lists create --data '<json>'` | Create audience |
| `lists update <LIST_ID> --data '<json>'` | Update audience |
| `lists delete <LIST_ID>` | Delete audience |
| `lists list-lists-id-members <LIST_ID>` | List members |
| `lists get-lists-id-members-id <LIST_ID> <SUBSCRIBER_HASH>` | Get member by MD5 hash |
| `lists create-lists-id-members <LIST_ID> --data '<json>'` | Add member |
| `lists list-lists-id-merge-fields <LIST_ID>` | List merge fields |
| `lists create-lists-id-merge-fields <LIST_ID> --data '<json>'` | Add merge field |
| `lists list-lists-id-segments <LIST_ID>` | List segments |
| `lists list-list-member-tags <LIST_ID> <SUBSCRIBER_HASH>` | List member tags |
| `lists create-list-member-tags <LIST_ID> <SUBSCRIBER_HASH> --data '<json>'` | Add/remove member tags |
| `lists list-lists-id-webhooks <LIST_ID>` | List webhooks |
| `lists create-lists-id-webhooks <LIST_ID> --data '<json>'` | Add webhook |
### Campaigns
| Command | Description |
|---|---|
| `campaigns list` | List campaigns |
| `campaigns get <CAMPAIGN_ID>` | Get campaign info |
| `campaigns create --data '<json>'` | Create campaign |
| `campaigns update <CAMPAIGN_ID> --data '<json>'` | Update campaign settings |
| `campaigns delete <CAMPAIGN_ID>` | Delete campaign |
| `campaigns send <CAMPAIGN_ID>` | Send campaign immediately |
| `campaigns schedule <CAMPAIGN_ID> --data '<json>'` | Schedule campaign |
| `campaigns cancel-send <CAMPAIGN_ID>` | Cancel scheduled send |
| `campaigns pause <CAMPAIGN_ID>` | Pause RSS campaign |
| `campaigns resume <CAMPAIGN_ID>` | Resume RSS campaign |
| `campaigns replicate <CAMPAIGN_ID>` | Duplicate campaign |
| `campaigns list-content <CAMPAIGN_ID>` | Get campaign content |
| `campaigns list-send-checklist <CAMPAIGN_ID>` | Pre-send checklist |
### Reports
| Command | Description |
|---|---|
| `reports list` | List all campaign reports |
| `reports get <CAMPAIGN_ID>` | Get campaign summary report |
| `reports list-email-activity <CAMPAIGN_ID>` | Per-subscriber open/click activity |
| `reports list-click-details <CAMPAIGN_ID>` | Link click breakdown |
| `reports list-open-details <CAMPAIGN_ID>` | Per-subscriber opens |
| `reports list-unsubscribed <CAMPAIGN_ID>` | Unsubscribers |
| `reports list-locations <CAMPAIGN_ID>` | Geographic breakdown |
| `reports list-domain-performance <CAMPAIGN_ID>` | Per-domain stats |
### Automations
| Command | Description |
|---|---|
| `automations list` | List automations |
| `automations get <WORKFLOW_ID>` | Get automation info |
| `automations create --data '<json>'` | Create automation |
| `automations pause <WORKFLOW_ID>` | Pause automation |
| `automations start <WORKFLOW_ID>` | Start automation |
| `automations archive <WORKFLOW_ID>` | Archive automation |
| `automations list-emails <WORKFLOW_ID>` | List automation emails |
### E-commerce
| Command | Description |
|---|---|
| `ecommerce list-ecommerce-stores` | List stores |
| `ecommerce get <STORE_ID>` | Get store info |
| `ecommerce create --data '<json>'` | Add store |
| `ecommerce list-ecommerce-stores-id-orders <STORE_ID>` | List orders |
| `ecommerce list-ecommerce-stores-id-products <STORE_ID>` | List products |
| `ecommerce list-ecommerce-stores-id-customers <STORE_ID>` | List customers |
| `ecommerce list-ecommerce-stores-id-carts <STORE_ID>` | List carts |
| `ecommerce list-ecommerce-stores-id-promocodes <PROMO_RULE_ID> <STORE_ID>` | List promo codes |
### Other Resources
| Group | Description |
|---|---|
| `templates` | Email templates (list, get, create, update, delete) |
| `template-folders` | Template folders |
| `campaign-folders` | Campaign folders |
| `file-manager` | Files and folders in the file manager |
| `landing-pages` | Landing pages (list, create, publish, unpublish) |
| `sms-campaigns` | SMS campaigns (10 operations) |
| `surveys` | Surveys (list, get, publish) |
| `reporting` | Facebook ad and landing page reporting |
| `search-campaigns` | Search campaigns by query |
| `search-members` | Search members across all audiences |
| `batches` | Batch API operations |
| `batch-webhooks` | Batch operation webhooks |
| `verified-domains` | Email domain verification |
| `authorized-apps` | OAuth connected apps |
| `connected-sites` | Connected site integrations |
| `conversations` | Inbox conversations |
| `activity-feed` | Account activity feed |
| `account-exports` | Account data exports |
## JSON Output
All commands support `--json` at the root level:
```bash
# List all audiences as JSON
cli-anything-mailchimp --json lists list
# Get a campaign report as JSON
cli-anything-mailchimp --json reports get abc123def
# Pipe to jq — use the native Mailchimp field name for the resource
cli-anything-mailchimp --json lists list | jq '.lists[].name'
cli-anything-mailchimp --json campaigns list | jq '.campaigns[].id'
```
**Envelope shapes** (native Mailchimp API response — use the resource-specific key):
```json
// List endpoints — key matches the resource name (lists, campaigns, members, etc.)
{"lists": [...], "total_items": 42, "_links": [...]}
{"campaigns": [...], "total_items": 10, "_links": [...]}
// Single resource GET / POST / PATCH
{"id": "abc123", "name": "My List", ...}
// DELETE
{"ok": true, "message": "Deleted."}
// Error
{"ok": false, "message": "Resource Not Found: ...", "data": {...}}
```
## Common Agent Patterns
```bash
# Get account health
cli-anything-mailchimp --json ping | jq '.health_status'
# List all audience IDs and names
cli-anything-mailchimp --json lists list | jq '.lists[] | {id, name}'
# Find all subscribed members in an audience
cli-anything-mailchimp --json lists list-lists-id-members <list_id> --status subscribed | jq '.members[].email_address'
# Create a campaign and get its send checklist
cli-anything-mailchimp --json campaigns create --data '{"type":"regular","settings":{"subject_line":"Hello","from_name":"Me","reply_to":"me@example.com"}}' | jq '.id'
cli-anything-mailchimp --json campaigns list-send-checklist <campaign_id> | jq '.items[] | select(.result == false)'
# Get unsubscribes for a sent campaign
cli-anything-mailchimp --json reports list-unsubscribed <campaign_id> | jq '.unsubscribes[].email_address'
# Add a member to an audience (subscriber hash = MD5 of lowercased email)
cli-anything-mailchimp --json lists create-members <list_id> --data '{"email_address":"user@example.com","status":"subscribed"}'
# Search for a member across all audiences
cli-anything-mailchimp --json search-members list --query "user@example.com" | jq '.exact_matches.members[]'
```
## Interactive REPL
Run `cli-anything-mailchimp` with no arguments to enter the REPL:
```
◆ cli-anything · Mailchimp
v0.1.0
Type help for commands, quit to exit
◆ mailchimp ❯ ping
✓ {"health_status": "Everything's Chimpy!"}
◆ mailchimp ❯ --json lists list
{"lists": [...], "total_items": 3, "_links": [...]}
◆ mailchimp ❯ quit
```
## Notes
- **Subscriber hash**: Mailchimp identifies members by the MD5 hash of their lowercased email. Compute with `python -c "import hashlib; email='email@example.com'; print(hashlib.md5(email.strip().lower().encode()).hexdigest())"`.
- **Body payloads**: All POST/PATCH/PUT commands accept `--data '<json>'`. See [Mailchimp API docs](https://mailchimp.com/developer/marketing/api/) for the schema of each endpoint.
- **Datacenter**: Your API key ends in `-us8`, `-eu2`, etc. The CLI extracts this automatically — include it in `MAILCHIMP_API_KEY`.
- **Rate limits**: The Marketing API rate-limits at 10 concurrent connections and a rolling per-account limit. For bulk operations, use the `batches` group.