adloop 0.27.0__tar.gz → 0.27.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {adloop-0.27.0 → adloop-0.27.1}/PKG-INFO +6 -5
- {adloop-0.27.0 → adloop-0.27.1}/README.md +5 -4
- {adloop-0.27.0 → adloop-0.27.1}/pyproject.toml +1 -1
- {adloop-0.27.0 → adloop-0.27.1}/pyproject.toml.orig +1 -1
- {adloop-0.27.0 → adloop-0.27.1}/src/adloop/cli.py +136 -58
- {adloop-0.27.0 → adloop-0.27.1}/src/adloop/__init__.py +0 -0
- {adloop-0.27.0 → adloop-0.27.1}/src/adloop/__main__.py +0 -0
- {adloop-0.27.0 → adloop-0.27.1}/src/adloop/_mcp_patches.py +0 -0
- {adloop-0.27.0 → adloop-0.27.1}/src/adloop/ads/__init__.py +0 -0
- {adloop-0.27.0 → adloop-0.27.1}/src/adloop/ads/ai_max.py +0 -0
- {adloop-0.27.0 → adloop-0.27.1}/src/adloop/ads/brands.py +0 -0
- {adloop-0.27.0 → adloop-0.27.1}/src/adloop/ads/client.py +0 -0
- {adloop-0.27.0 → adloop-0.27.1}/src/adloop/ads/conversion_actions.py +0 -0
- {adloop-0.27.0 → adloop-0.27.1}/src/adloop/ads/currency.py +0 -0
- {adloop-0.27.0 → adloop-0.27.1}/src/adloop/ads/custom_conversion_goals.py +0 -0
- {adloop-0.27.0 → adloop-0.27.1}/src/adloop/ads/enums.py +0 -0
- {adloop-0.27.0 → adloop-0.27.1}/src/adloop/ads/forecast.py +0 -0
- {adloop-0.27.0 → adloop-0.27.1}/src/adloop/ads/gaql.py +0 -0
- {adloop-0.27.0 → adloop-0.27.1}/src/adloop/ads/image_fetch.py +0 -0
- {adloop-0.27.0 → adloop-0.27.1}/src/adloop/ads/pmax.py +0 -0
- {adloop-0.27.0 → adloop-0.27.1}/src/adloop/ads/read.py +0 -0
- {adloop-0.27.0 → adloop-0.27.1}/src/adloop/ads/shopping.py +0 -0
- {adloop-0.27.0 → adloop-0.27.1}/src/adloop/ads/validate_only.py +0 -0
- {adloop-0.27.0 → adloop-0.27.1}/src/adloop/ads/write.py +0 -0
- {adloop-0.27.0 → adloop-0.27.1}/src/adloop/auth.py +0 -0
- {adloop-0.27.0 → adloop-0.27.1}/src/adloop/config.py +0 -0
- {adloop-0.27.0 → adloop-0.27.1}/src/adloop/crossref.py +0 -0
- {adloop-0.27.0 → adloop-0.27.1}/src/adloop/diagnostics.py +0 -0
- {adloop-0.27.0 → adloop-0.27.1}/src/adloop/ga4/__init__.py +0 -0
- {adloop-0.27.0 → adloop-0.27.1}/src/adloop/ga4/client.py +0 -0
- {adloop-0.27.0 → adloop-0.27.1}/src/adloop/ga4/reports.py +0 -0
- {adloop-0.27.0 → adloop-0.27.1}/src/adloop/ga4/tracking.py +0 -0
- {adloop-0.27.0 → adloop-0.27.1}/src/adloop/ga4/write.py +0 -0
- {adloop-0.27.0 → adloop-0.27.1}/src/adloop/gsc/__init__.py +0 -0
- {adloop-0.27.0 → adloop-0.27.1}/src/adloop/gsc/client.py +0 -0
- {adloop-0.27.0 → adloop-0.27.1}/src/adloop/gsc/reports.py +0 -0
- {adloop-0.27.0 → adloop-0.27.1}/src/adloop/gtm/__init__.py +0 -0
- {adloop-0.27.0 → adloop-0.27.1}/src/adloop/gtm/client.py +0 -0
- {adloop-0.27.0 → adloop-0.27.1}/src/adloop/gtm/entities.py +0 -0
- {adloop-0.27.0 → adloop-0.27.1}/src/adloop/gtm/read.py +0 -0
- {adloop-0.27.0 → adloop-0.27.1}/src/adloop/gtm/write.py +0 -0
- {adloop-0.27.0 → adloop-0.27.1}/src/adloop/merchant/__init__.py +0 -0
- {adloop-0.27.0 → adloop-0.27.1}/src/adloop/merchant/client.py +0 -0
- {adloop-0.27.0 → adloop-0.27.1}/src/adloop/merchant/read.py +0 -0
- {adloop-0.27.0 → adloop-0.27.1}/src/adloop/pagespeed.py +0 -0
- {adloop-0.27.0 → adloop-0.27.1}/src/adloop/reddit/__init__.py +0 -0
- {adloop-0.27.0 → adloop-0.27.1}/src/adloop/reddit/auth.py +0 -0
- {adloop-0.27.0 → adloop-0.27.1}/src/adloop/reddit/client.py +0 -0
- {adloop-0.27.0 → adloop-0.27.1}/src/adloop/reddit/read.py +0 -0
- {adloop-0.27.0 → adloop-0.27.1}/src/adloop/reddit/schedule.py +0 -0
- {adloop-0.27.0 → adloop-0.27.1}/src/adloop/reddit/write.py +0 -0
- {adloop-0.27.0 → adloop-0.27.1}/src/adloop/rules/__init__.py +0 -0
- {adloop-0.27.0 → adloop-0.27.1}/src/adloop/rules/adloop.md +0 -0
- {adloop-0.27.0 → adloop-0.27.1}/src/adloop/rules/commands/analyze-performance.md +0 -0
- {adloop-0.27.0 → adloop-0.27.1}/src/adloop/rules/commands/budget-plan.md +0 -0
- {adloop-0.27.0 → adloop-0.27.1}/src/adloop/rules/commands/create-ad.md +0 -0
- {adloop-0.27.0 → adloop-0.27.1}/src/adloop/rules/commands/create-campaign.md +0 -0
- {adloop-0.27.0 → adloop-0.27.1}/src/adloop/rules/commands/diagnose-tracking.md +0 -0
- {adloop-0.27.0 → adloop-0.27.1}/src/adloop/rules/commands/optimize-campaign.md +0 -0
- {adloop-0.27.0 → adloop-0.27.1}/src/adloop/rules_install.py +0 -0
- {adloop-0.27.0 → adloop-0.27.1}/src/adloop/runtime.py +0 -0
- {adloop-0.27.0 → adloop-0.27.1}/src/adloop/safety/__init__.py +0 -0
- {adloop-0.27.0 → adloop-0.27.1}/src/adloop/safety/approval.py +0 -0
- {adloop-0.27.0 → adloop-0.27.1}/src/adloop/safety/approval_card.html +0 -0
- {adloop-0.27.0 → adloop-0.27.1}/src/adloop/safety/audit.py +0 -0
- {adloop-0.27.0 → adloop-0.27.1}/src/adloop/safety/guards.py +0 -0
- {adloop-0.27.0 → adloop-0.27.1}/src/adloop/safety/preview.py +0 -0
- {adloop-0.27.0 → adloop-0.27.1}/src/adloop/server.py +0 -0
- {adloop-0.27.0 → adloop-0.27.1}/src/adloop/tracking.py +0 -0
- {adloop-0.27.0 → adloop-0.27.1}/src/adloop/upload_card.html +0 -0
- {adloop-0.27.0 → adloop-0.27.1}/src/adloop/uploads.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: adloop
|
|
3
|
-
Version: 0.27.
|
|
3
|
+
Version: 0.27.1
|
|
4
4
|
Summary: The AI command center for Google Ads, Reddit Ads, GA4, and tracking code.
|
|
5
5
|
Keywords: mcp,google-ads,reddit-ads,google-analytics,ga4,cursor,marketing
|
|
6
6
|
Author: Daniel Klose
|
|
@@ -404,7 +404,7 @@ uv run adloop init
|
|
|
404
404
|
|
|
405
405
|
### What `adloop init` does
|
|
406
406
|
|
|
407
|
-
The wizard walks you through:
|
|
407
|
+
The wizard first asks whether to set up Google. Answer no to run AdLoop on Reddit Ads alone: it skips steps 1 to 6 and goes straight to the Reddit step. Otherwise it walks you through:
|
|
408
408
|
|
|
409
409
|
1. **Google Cloud setup** — creates a project, enables the three APIs, generates an OAuth client (see [Custom Google Cloud Project Setup](#custom-google-cloud-project-setup) below for the exact steps the wizard refers you to)
|
|
410
410
|
2. **Google Ads API access** — applied for on your Cloud project's [Google Ads API Overview page](https://console.cloud.google.com/google/ads-apis/overview); the legacy developer-token prompt can be left empty
|
|
@@ -412,9 +412,10 @@ The wizard walks you through:
|
|
|
412
412
|
4. **OAuth sign-in** — opens a browser to sign in with Google (or prints a URL for headless servers)
|
|
413
413
|
5. **Auto-discovers your accounts** — finds your GA4 properties and Ads accounts automatically
|
|
414
414
|
6. **Optional services** — pin a GTM container, a Search Console property (both auto-discovered too), and a PageSpeed API key; skip any of them with Enter
|
|
415
|
-
7. **
|
|
416
|
-
8. **
|
|
417
|
-
9. **
|
|
415
|
+
7. **Reddit Ads** — optional next to Google, required without it: your own Reddit developer app, sign-in, and the default ad account
|
|
416
|
+
8. **Safety defaults** — budget cap and dry-run preference
|
|
417
|
+
9. **Toolsets** — optionally expose only part of the tool catalog to your AI client (see [Toolsets](#toolsets--trim-the-context-footprint))
|
|
418
|
+
10. **Editor config snippets** — prints MCP configuration for both Cursor and Claude Code, including your toolset selection
|
|
418
419
|
|
|
419
420
|
### Requirements
|
|
420
421
|
|
|
@@ -376,7 +376,7 @@ uv run adloop init
|
|
|
376
376
|
|
|
377
377
|
### What `adloop init` does
|
|
378
378
|
|
|
379
|
-
The wizard walks you through:
|
|
379
|
+
The wizard first asks whether to set up Google. Answer no to run AdLoop on Reddit Ads alone: it skips steps 1 to 6 and goes straight to the Reddit step. Otherwise it walks you through:
|
|
380
380
|
|
|
381
381
|
1. **Google Cloud setup** — creates a project, enables the three APIs, generates an OAuth client (see [Custom Google Cloud Project Setup](#custom-google-cloud-project-setup) below for the exact steps the wizard refers you to)
|
|
382
382
|
2. **Google Ads API access** — applied for on your Cloud project's [Google Ads API Overview page](https://console.cloud.google.com/google/ads-apis/overview); the legacy developer-token prompt can be left empty
|
|
@@ -384,9 +384,10 @@ The wizard walks you through:
|
|
|
384
384
|
4. **OAuth sign-in** — opens a browser to sign in with Google (or prints a URL for headless servers)
|
|
385
385
|
5. **Auto-discovers your accounts** — finds your GA4 properties and Ads accounts automatically
|
|
386
386
|
6. **Optional services** — pin a GTM container, a Search Console property (both auto-discovered too), and a PageSpeed API key; skip any of them with Enter
|
|
387
|
-
7. **
|
|
388
|
-
8. **
|
|
389
|
-
9. **
|
|
387
|
+
7. **Reddit Ads** — optional next to Google, required without it: your own Reddit developer app, sign-in, and the default ad account
|
|
388
|
+
8. **Safety defaults** — budget cap and dry-run preference
|
|
389
|
+
9. **Toolsets** — optionally expose only part of the tool catalog to your AI client (see [Toolsets](#toolsets--trim-the-context-footprint))
|
|
390
|
+
10. **Editor config snippets** — prints MCP configuration for both Cursor and Claude Code, including your toolset selection
|
|
390
391
|
|
|
391
392
|
### Requirements
|
|
392
393
|
|
|
@@ -414,7 +414,7 @@ def _wizard_gsc_step(oauth_ok: bool, _existing) -> str:
|
|
|
414
414
|
)
|
|
415
415
|
|
|
416
416
|
|
|
417
|
-
def _wizard_reddit_step(_existing) -> dict[str, str]:
|
|
417
|
+
def _wizard_reddit_step(_existing, required: bool = False) -> dict[str, str]:
|
|
418
418
|
"""Optionally connect Reddit Ads: own developer app, own OAuth, own token file.
|
|
419
419
|
|
|
420
420
|
Returns the ``reddit:`` values for the config. Reddit's redirect URI
|
|
@@ -430,7 +430,8 @@ def _wizard_reddit_step(_existing) -> dict[str, str]:
|
|
|
430
430
|
"ad_account_id": _existing("reddit", "ad_account_id"),
|
|
431
431
|
"username": _existing("reddit", "username"),
|
|
432
432
|
}
|
|
433
|
-
|
|
433
|
+
# Without Google, Reddit is the platform being set up: no opt-out.
|
|
434
|
+
if not required and not _prompt_bool("Connect Reddit Ads? (optional)", default=bool(existing_id)):
|
|
434
435
|
return values
|
|
435
436
|
|
|
436
437
|
_print()
|
|
@@ -589,6 +590,70 @@ def run_init_wizard() -> None:
|
|
|
589
590
|
return str(existing_config[section].get(key, fallback))
|
|
590
591
|
return fallback
|
|
591
592
|
|
|
593
|
+
# Both platforms are optional; a workspace can run on Reddit alone.
|
|
594
|
+
_print(" AdLoop connects Google (Ads, Analytics, Tag Manager, Search Console)")
|
|
595
|
+
_print(" and Reddit Ads. Set up one or both.")
|
|
596
|
+
_print()
|
|
597
|
+
use_google = _prompt_bool(
|
|
598
|
+
"Set up Google?", default=_existing("google", "credentials_path") != "" or existing_config is None
|
|
599
|
+
)
|
|
600
|
+
credentials_path = project_id = developer_token = login_customer_id = ""
|
|
601
|
+
step_num = 0
|
|
602
|
+
if use_google:
|
|
603
|
+
step_num = _wizard_google_steps(_existing)
|
|
604
|
+
credentials_path, project_id, developer_token, login_customer_id = _WIZARD_GOOGLE
|
|
605
|
+
|
|
606
|
+
# OAuth + auto-discovery
|
|
607
|
+
step_num += 1
|
|
608
|
+
_step_header(step_num, "Authorization & Account Discovery" if use_google else "Writing a first config")
|
|
609
|
+
|
|
610
|
+
# Write a temporary config for OAuth + discovery. If the wizard is
|
|
611
|
+
# interrupted after this point, _cleanup_on_cancel restores the original.
|
|
612
|
+
_ADLOOP_DIR.mkdir(parents=True, exist_ok=True)
|
|
613
|
+
temp_config_yaml = _generate_config_yaml(
|
|
614
|
+
project_id=project_id,
|
|
615
|
+
credentials_path=credentials_path,
|
|
616
|
+
property_id="",
|
|
617
|
+
developer_token=developer_token,
|
|
618
|
+
customer_id="",
|
|
619
|
+
login_customer_id=login_customer_id,
|
|
620
|
+
max_daily_budget=50.0,
|
|
621
|
+
require_dry_run=True,
|
|
622
|
+
)
|
|
623
|
+
_CONFIG_PATH.write_text(temp_config_yaml)
|
|
624
|
+
|
|
625
|
+
# Everything below uses the temp config for OAuth and discovery.
|
|
626
|
+
# If the wizard is interrupted, restore the original config (or remove
|
|
627
|
+
# the temp) so we never leave a half-baked config on disk.
|
|
628
|
+
try:
|
|
629
|
+
_run_wizard_post_config(
|
|
630
|
+
credentials_path=credentials_path,
|
|
631
|
+
project_id=project_id,
|
|
632
|
+
developer_token=developer_token,
|
|
633
|
+
login_customer_id=login_customer_id,
|
|
634
|
+
step_num=step_num,
|
|
635
|
+
_existing=_existing,
|
|
636
|
+
use_google=use_google,
|
|
637
|
+
)
|
|
638
|
+
except KeyboardInterrupt:
|
|
639
|
+
if _original_config_backup is not None:
|
|
640
|
+
_CONFIG_PATH.write_text(_original_config_backup)
|
|
641
|
+
elif _CONFIG_PATH.exists():
|
|
642
|
+
_CONFIG_PATH.unlink()
|
|
643
|
+
raise
|
|
644
|
+
|
|
645
|
+
|
|
646
|
+
_WIZARD_GOOGLE: tuple[str, str, str, str] = ("", "", "", "")
|
|
647
|
+
|
|
648
|
+
|
|
649
|
+
def _wizard_google_steps(_existing) -> int:
|
|
650
|
+
"""The Google project, credentials and API access steps; returns the last step number.
|
|
651
|
+
|
|
652
|
+
The collected values land in ``_WIZARD_GOOGLE`` (credentials path,
|
|
653
|
+
project id, developer token, login customer id).
|
|
654
|
+
"""
|
|
655
|
+
global _WIZARD_GOOGLE
|
|
656
|
+
|
|
592
657
|
# Step 1: Google Cloud project
|
|
593
658
|
#
|
|
594
659
|
# AdLoop no longer ships OAuth credentials. Every install brings its
|
|
@@ -641,44 +706,8 @@ def run_init_wizard() -> None:
|
|
|
641
706
|
default=_existing("ads", "login_customer_id"),
|
|
642
707
|
required=False,
|
|
643
708
|
)
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
step_num += 1
|
|
647
|
-
_step_header(step_num, "Authorization & Account Discovery")
|
|
648
|
-
|
|
649
|
-
# Write a temporary config for OAuth + discovery. If the wizard is
|
|
650
|
-
# interrupted after this point, _cleanup_on_cancel restores the original.
|
|
651
|
-
_ADLOOP_DIR.mkdir(parents=True, exist_ok=True)
|
|
652
|
-
temp_config_yaml = _generate_config_yaml(
|
|
653
|
-
project_id=project_id,
|
|
654
|
-
credentials_path=credentials_path,
|
|
655
|
-
property_id="",
|
|
656
|
-
developer_token=developer_token,
|
|
657
|
-
customer_id="",
|
|
658
|
-
login_customer_id=login_customer_id,
|
|
659
|
-
max_daily_budget=50.0,
|
|
660
|
-
require_dry_run=True,
|
|
661
|
-
)
|
|
662
|
-
_CONFIG_PATH.write_text(temp_config_yaml)
|
|
663
|
-
|
|
664
|
-
# Everything below uses the temp config for OAuth and discovery.
|
|
665
|
-
# If the wizard is interrupted, restore the original config (or remove
|
|
666
|
-
# the temp) so we never leave a half-baked config on disk.
|
|
667
|
-
try:
|
|
668
|
-
_run_wizard_post_config(
|
|
669
|
-
credentials_path=credentials_path,
|
|
670
|
-
project_id=project_id,
|
|
671
|
-
developer_token=developer_token,
|
|
672
|
-
login_customer_id=login_customer_id,
|
|
673
|
-
step_num=step_num,
|
|
674
|
-
_existing=_existing,
|
|
675
|
-
)
|
|
676
|
-
except KeyboardInterrupt:
|
|
677
|
-
if _original_config_backup is not None:
|
|
678
|
-
_CONFIG_PATH.write_text(_original_config_backup)
|
|
679
|
-
elif _CONFIG_PATH.exists():
|
|
680
|
-
_CONFIG_PATH.unlink()
|
|
681
|
-
raise
|
|
709
|
+
_WIZARD_GOOGLE = (credentials_path, project_id, developer_token, login_customer_id)
|
|
710
|
+
return step_num
|
|
682
711
|
|
|
683
712
|
|
|
684
713
|
def _run_wizard_post_config(
|
|
@@ -689,10 +718,9 @@ def _run_wizard_post_config(
|
|
|
689
718
|
login_customer_id: str,
|
|
690
719
|
step_num: int,
|
|
691
720
|
_existing: object,
|
|
721
|
+
use_google: bool = True,
|
|
692
722
|
) -> None:
|
|
693
723
|
"""Run the wizard steps after the temp config has been written."""
|
|
694
|
-
from adloop.config import load_config
|
|
695
|
-
|
|
696
724
|
# Optional: copy custom credentials to ~/.adloop/
|
|
697
725
|
if credentials_path:
|
|
698
726
|
creds_expanded = Path(credentials_path).expanduser()
|
|
@@ -706,6 +734,54 @@ def _run_wizard_post_config(
|
|
|
706
734
|
shutil.copy2(creds_expanded, adloop_creds)
|
|
707
735
|
_print(f" ✓ Credentials copied to {adloop_creds}")
|
|
708
736
|
|
|
737
|
+
oauth_ok = False
|
|
738
|
+
property_id = ""
|
|
739
|
+
customer_id = ""
|
|
740
|
+
gtm_account_id = gtm_container_id = gsc_site_url = ""
|
|
741
|
+
if use_google:
|
|
742
|
+
oauth_ok, property_id, customer_id, step_num = _wizard_google_discovery(step_num, _existing)
|
|
743
|
+
|
|
744
|
+
# Optional service pins: GTM / Search Console / PageSpeed. All three
|
|
745
|
+
# work without config (explicit args or discovery per call) — a pin
|
|
746
|
+
# just saves the model a lookup and locks tools to the right property.
|
|
747
|
+
step_num += 1
|
|
748
|
+
if use_google:
|
|
749
|
+
_step_header(step_num, "Optional Services (GTM, Search Console, PageSpeed)")
|
|
750
|
+
gtm_account_id, gtm_container_id = _wizard_gtm_step(oauth_ok, _existing)
|
|
751
|
+
gsc_site_url = _wizard_gsc_step(oauth_ok, _existing)
|
|
752
|
+
else:
|
|
753
|
+
_step_header(step_num, "PageSpeed (optional)")
|
|
754
|
+
pagespeed_api_key = _prompt(
|
|
755
|
+
"PageSpeed Insights API key (optional, Enter to skip)",
|
|
756
|
+
default=_existing("pagespeed", "api_key"), # type: ignore[operator]
|
|
757
|
+
required=False,
|
|
758
|
+
)
|
|
759
|
+
|
|
760
|
+
# Reddit Ads: a second ad platform with its own OAuth app and token file.
|
|
761
|
+
step_num += 1
|
|
762
|
+
_step_header(step_num, "Reddit Ads" if not use_google else "Reddit Ads (optional)")
|
|
763
|
+
reddit_values = _wizard_reddit_step(_existing, required=not use_google)
|
|
764
|
+
_wizard_finish(
|
|
765
|
+
step_num=step_num,
|
|
766
|
+
_existing=_existing,
|
|
767
|
+
credentials_path=credentials_path,
|
|
768
|
+
project_id=project_id,
|
|
769
|
+
developer_token=developer_token,
|
|
770
|
+
login_customer_id=login_customer_id,
|
|
771
|
+
property_id=property_id,
|
|
772
|
+
customer_id=customer_id,
|
|
773
|
+
gtm_account_id=gtm_account_id,
|
|
774
|
+
gtm_container_id=gtm_container_id,
|
|
775
|
+
gsc_site_url=gsc_site_url,
|
|
776
|
+
pagespeed_api_key=pagespeed_api_key,
|
|
777
|
+
reddit_values=reddit_values,
|
|
778
|
+
)
|
|
779
|
+
|
|
780
|
+
|
|
781
|
+
def _wizard_google_discovery(step_num: int, _existing) -> tuple[bool, str, str, int]:
|
|
782
|
+
"""Google sign-in plus GA4 and Ads account discovery: (oauth_ok, property_id, customer_id, step)."""
|
|
783
|
+
from adloop.config import load_config
|
|
784
|
+
|
|
709
785
|
_print(" Signing in with Google (this may open a browser)...")
|
|
710
786
|
_print()
|
|
711
787
|
oauth_ok = False
|
|
@@ -797,24 +873,26 @@ def _run_wizard_post_config(
|
|
|
797
873
|
default=_existing("ads", "customer_id"), # type: ignore[operator]
|
|
798
874
|
)
|
|
799
875
|
|
|
800
|
-
|
|
801
|
-
# work without config (explicit args or discovery per call) — a pin
|
|
802
|
-
# just saves the model a lookup and locks tools to the right property.
|
|
803
|
-
step_num += 1
|
|
804
|
-
_step_header(step_num, "Optional Services (GTM, Search Console, PageSpeed)")
|
|
805
|
-
gtm_account_id, gtm_container_id = _wizard_gtm_step(oauth_ok, _existing)
|
|
806
|
-
gsc_site_url = _wizard_gsc_step(oauth_ok, _existing)
|
|
807
|
-
pagespeed_api_key = _prompt(
|
|
808
|
-
"PageSpeed Insights API key (optional, Enter to skip)",
|
|
809
|
-
default=_existing("pagespeed", "api_key"), # type: ignore[operator]
|
|
810
|
-
required=False,
|
|
811
|
-
)
|
|
876
|
+
return oauth_ok, property_id, customer_id, step_num
|
|
812
877
|
|
|
813
|
-
# Reddit Ads: a second ad platform with its own OAuth app and token file.
|
|
814
|
-
step_num += 1
|
|
815
|
-
_step_header(step_num, "Reddit Ads (optional)")
|
|
816
|
-
reddit_values = _wizard_reddit_step(_existing)
|
|
817
878
|
|
|
879
|
+
def _wizard_finish(
|
|
880
|
+
*,
|
|
881
|
+
step_num: int,
|
|
882
|
+
_existing,
|
|
883
|
+
credentials_path: str,
|
|
884
|
+
project_id: str,
|
|
885
|
+
developer_token: str,
|
|
886
|
+
login_customer_id: str,
|
|
887
|
+
property_id: str,
|
|
888
|
+
customer_id: str,
|
|
889
|
+
gtm_account_id: str,
|
|
890
|
+
gtm_container_id: str,
|
|
891
|
+
gsc_site_url: str,
|
|
892
|
+
pagespeed_api_key: str,
|
|
893
|
+
reddit_values: dict,
|
|
894
|
+
) -> None:
|
|
895
|
+
"""Safety defaults, the final config and the client snippets."""
|
|
818
896
|
# Safety defaults
|
|
819
897
|
step_num += 1
|
|
820
898
|
_step_header(step_num, "Safety Defaults")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|