direct-cli 0.3.14__tar.gz → 0.3.16__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.
- {direct_cli-0.3.14 → direct_cli-0.3.16}/.github/workflows/claude.yml +1 -1
- {direct_cli-0.3.14 → direct_cli-0.3.16}/CHANGELOG.md +55 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/PKG-INFO +10 -2
- {direct_cli-0.3.14 → direct_cli-0.3.16}/README.md +9 -1
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli/_vendor/tapi_yandex_direct/v4/adapter.py +9 -3
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli/cli.py +28 -0
- direct_cli-0.3.16/direct_cli/commands/v4events.py +232 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli/commands/v4finance.py +39 -38
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli/commands/v4forecast.py +52 -3
- direct_cli-0.3.16/direct_cli/i18n.py +107 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli/v4/__init__.py +2 -2
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli/v4_contracts.py +134 -35
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli.egg-info/PKG-INFO +10 -2
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli.egg-info/SOURCES.txt +48 -0
- direct_cli-0.3.16/docs/audits/API_COVERAGE.md +93 -0
- direct_cli-0.3.16/docs/audits/PROJECT_WIRE_SHAPE_AUDIT_2026-05-29.md +193 -0
- direct_cli-0.3.16/docs/audits/wire_shape.json +1394 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/pyproject.toml +1 -1
- direct_cli-0.3.16/scripts/audit_wire_shape.py +791 -0
- direct_cli-0.3.16/scripts/preflight_check.sh +62 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/scripts/test_dangerous_commands.sh +2 -2
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/API_COVERAGE.md +11 -5
- direct_cli-0.3.16/tests/cassettes/test_read_cassettes/test_read_command[adextensions_get].yaml +56 -0
- direct_cli-0.3.16/tests/cassettes/test_read_cassettes/test_read_command[adgroups_get].yaml +56 -0
- direct_cli-0.3.16/tests/cassettes/test_read_cassettes/test_read_command[adimages_get].yaml +66 -0
- direct_cli-0.3.16/tests/cassettes/test_read_cassettes/test_read_command[ads_get].yaml +56 -0
- direct_cli-0.3.16/tests/cassettes/test_read_cassettes/test_read_command[advideos_get].yaml +56 -0
- direct_cli-0.3.16/tests/cassettes/test_read_cassettes/test_read_command[audiencetargets_get].yaml +56 -0
- direct_cli-0.3.16/tests/cassettes/test_read_cassettes/test_read_command[bidmodifiers_get].yaml +56 -0
- direct_cli-0.3.16/tests/cassettes/test_read_cassettes/test_read_command[bids_get].yaml +56 -0
- direct_cli-0.3.16/tests/cassettes/test_read_cassettes/test_read_command[businesses_get].yaml +56 -0
- direct_cli-0.3.16/tests/cassettes/test_read_cassettes/test_read_command[campaigns_get].yaml +57 -0
- direct_cli-0.3.16/tests/cassettes/test_read_cassettes/test_read_command[changes_check].yaml +56 -0
- direct_cli-0.3.16/tests/cassettes/test_read_cassettes/test_read_command[changes_check_campaigns].yaml +56 -0
- direct_cli-0.3.16/tests/cassettes/test_read_cassettes/test_read_command[changes_check_dictionaries].yaml +56 -0
- direct_cli-0.3.16/tests/cassettes/test_read_cassettes/test_read_command[clients_get].yaml +56 -0
- direct_cli-0.3.16/tests/cassettes/test_read_cassettes/test_read_command[creatives_get].yaml +56 -0
- direct_cli-0.3.16/tests/cassettes/test_read_cassettes/test_read_command[dictionaries_get].yaml +11696 -0
- direct_cli-0.3.16/tests/cassettes/test_read_cassettes/test_read_command[dynamicads_get].yaml +56 -0
- direct_cli-0.3.16/tests/cassettes/test_read_cassettes/test_read_command[dynamicfeedadtargets_get].yaml +56 -0
- direct_cli-0.3.16/tests/cassettes/test_read_cassettes/test_read_command[feeds_get].yaml +56 -0
- direct_cli-0.3.16/tests/cassettes/test_read_cassettes/test_read_command[keywordbids_get].yaml +56 -0
- direct_cli-0.3.16/tests/cassettes/test_read_cassettes/test_read_command[keywords_get].yaml +56 -0
- direct_cli-0.3.16/tests/cassettes/test_read_cassettes/test_read_command[keywordsresearch_deduplicate].yaml +59 -0
- direct_cli-0.3.16/tests/cassettes/test_read_cassettes/test_read_command[keywordsresearch_has_search_volume].yaml +58 -0
- direct_cli-0.3.16/tests/cassettes/test_read_cassettes/test_read_command[leads_get].yaml +56 -0
- direct_cli-0.3.16/tests/cassettes/test_read_cassettes/test_read_command[negativekeywordsharedsets_get].yaml +56 -0
- direct_cli-0.3.16/tests/cassettes/test_read_cassettes/test_read_command[reports_get].yaml +64 -0
- direct_cli-0.3.16/tests/cassettes/test_read_cassettes/test_read_command[retargeting_get].yaml +56 -0
- direct_cli-0.3.16/tests/cassettes/test_read_cassettes/test_read_command[sitelinks_get].yaml +56 -0
- direct_cli-0.3.16/tests/cassettes/test_read_cassettes/test_read_command[smartadtargets_get].yaml +56 -0
- direct_cli-0.3.16/tests/cassettes/test_read_cassettes/test_read_command[strategies_get].yaml +66 -0
- direct_cli-0.3.16/tests/cassettes/test_read_cassettes/test_read_command[turbopages_get].yaml +56 -0
- direct_cli-0.3.16/tests/cassettes/test_read_cassettes/test_read_command[v4events_get_events_log].yaml +52 -0
- direct_cli-0.3.16/tests/cassettes/test_read_cassettes/test_read_command[v4finance_get_clients_units].yaml +52 -0
- direct_cli-0.3.16/tests/cassettes/test_read_cassettes/test_read_command[v4forecast_list].yaml +52 -0
- direct_cli-0.3.16/tests/cassettes/test_read_cassettes/test_read_command[v4goals_get_stat_goals].yaml +52 -0
- direct_cli-0.3.16/tests/cassettes/test_read_cassettes/test_read_command[v4tags_get_campaigns].yaml +52 -0
- direct_cli-0.3.16/tests/cassettes/test_read_cassettes/test_read_command[v4wordstat_list_reports].yaml +52 -0
- direct_cli-0.3.16/tests/cassettes/test_read_cassettes/test_read_command[vcards_get].yaml +57 -0
- direct_cli-0.3.16/tests/cassettes/test_read_cassettes/test_v4finance_check_payment_unknown_transaction.yaml +52 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/conftest.py +68 -17
- direct_cli-0.3.16/tests/test_audit_wire_shape.py +158 -0
- direct_cli-0.3.16/tests/test_i18n.py +118 -0
- direct_cli-0.3.16/tests/test_read_cassettes.py +252 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/test_v4_contracts.py +16 -1
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/test_v4_runtime_shape.py +29 -3
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/test_v4events.py +110 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/test_v4finance_money.py +80 -13
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/test_v4forecast.py +68 -1
- direct_cli-0.3.14/direct_cli/commands/v4events.py +0 -120
- {direct_cli-0.3.14 → direct_cli-0.3.16}/.env.example +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/.github/copilot-instructions.md +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/.github/workflows/api-coverage.yml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/.github/workflows/quality.yml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/.gitignore +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/AGENTS.md +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/CLAUDE.md +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/MANIFEST.in +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli/__init__.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli/_bidding_strategy.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli/_deprecated.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli/_smoke_probes.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli/_vendor/__init__.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli/_vendor/tapi_yandex_direct/__init__.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli/_vendor/tapi_yandex_direct/endpoints.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli/_vendor/tapi_yandex_direct/exceptions.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli/_vendor/tapi_yandex_direct/resource_mapping.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli/_vendor/tapi_yandex_direct/tapi_yandex_direct.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli/_vendor/tapi_yandex_direct/tapi_yandex_direct.pyi +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli/_vendor/tapi_yandex_direct/v4/__init__.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli/_vendor/tapi_yandex_direct/v4/adapter.pyi +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli/_vendor/tapi_yandex_direct/v4/resource_mapping.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli/api.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli/auth.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli/commands/__init__.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli/commands/adextensions.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli/commands/adgroups.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli/commands/adimages.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli/commands/ads.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli/commands/advideos.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli/commands/agencyclients.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli/commands/audiencetargets.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli/commands/auth.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli/commands/balance.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli/commands/bidmodifiers.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli/commands/bids.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli/commands/businesses.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli/commands/campaigns.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli/commands/changes.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli/commands/clients.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli/commands/creatives.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli/commands/dictionaries.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli/commands/dynamicads.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli/commands/dynamicfeedadtargets.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli/commands/feeds.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli/commands/keywordbids.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli/commands/keywords.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli/commands/keywordsresearch.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli/commands/leads.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli/commands/negativekeywordsharedsets.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli/commands/reports.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli/commands/retargeting.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli/commands/sitelinks.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli/commands/smartadtargets.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli/commands/strategies.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli/commands/turbopages.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli/commands/v4account.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli/commands/v4goals.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli/commands/v4shells.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli/commands/v4tags.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli/commands/v4wordstat.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli/commands/vcards.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli/output.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli/reports_coverage.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli/smoke_matrix.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli/utils.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli/v4/money.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli/wsdl_coverage.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli.egg-info/dependency_links.txt +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli.egg-info/entry_points.txt +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli.egg-info/requires.txt +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/direct_cli.egg-info/top_level.txt +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/docs/audits/issue-198-mutating-wsdl-audit.md +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/docs/superpowers/plans/2026-04-12-issue-32-completion.md +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/docs/superpowers/specs/2026-04-23-vendor-tapi-yandex-direct-design.md +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/scripts/anonymize_cassettes.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/scripts/build_api_coverage_checklist.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/scripts/build_api_coverage_report.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/scripts/build_wsdl_optional_field_audit.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/scripts/check_all_docs_urls.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/scripts/check_reports_drift.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/scripts/check_wsdl_drift.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/scripts/patch_vendor_imports.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/scripts/refresh_reports_cache.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/scripts/refresh_wsdl_cache.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/scripts/release_pypi.sh +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/scripts/sandbox_write_audit.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/scripts/sandbox_write_live.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/scripts/test_safe_commands.sh +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/scripts/test_sandbox_write.sh +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/scripts/update_vendor.sh +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/setup.cfg +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/setup.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/API_ISSUE_AUDIT.md +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/MANUAL_COVERAGE.md +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/WSDL_OPTIONAL_FIELD_AUDIT.md +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/__init__.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/_orphan_store.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/api_coverage_payloads.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/cassettes/test_integration_write/TestWriteAdExtensions.test_add_delete.yaml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/cassettes/test_integration_write/TestWriteAdGroups.test_add_update_delete.yaml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/cassettes/test_integration_write/TestWriteAdImages.test_add_delete.yaml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/cassettes/test_integration_write/TestWriteAds.test_add_text_ad_update_delete.yaml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/cassettes/test_integration_write/TestWriteAudienceTargets.test_add_delete.yaml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/cassettes/test_integration_write/TestWriteBidModifiersAdd.test_add_delete_mobile.yaml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/cassettes/test_integration_write/TestWriteBidModifiersSet.test_set_without_id_is_rejected.yaml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/cassettes/test_integration_write/TestWriteBids.test_set_bid.yaml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/cassettes/test_integration_write/TestWriteBidsRead.test_bids_get.yaml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/cassettes/test_integration_write/TestWriteBidsRead.test_bids_set_auto.yaml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/cassettes/test_integration_write/TestWriteCampaignDraftLifecycle.test_draft_create_get_delete.yaml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/cassettes/test_integration_write/TestWriteCampaigns.test_campaign_lifecycle.yaml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/cassettes/test_integration_write/TestWriteDynamicAds.test_add_update_delete.yaml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/cassettes/test_integration_write/TestWriteFeeds.test_add_update_delete.yaml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/cassettes/test_integration_write/TestWriteKeywordBids.test_set_keyword_bid.yaml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/cassettes/test_integration_write/TestWriteKeywords.test_add_update_delete.yaml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/cassettes/test_integration_write/TestWriteNegativeKeywordSharedSets.test_add_update_delete.yaml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/cassettes/test_integration_write/TestWriteRetargeting.test_add_delete.yaml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/cassettes/test_integration_write/TestWriteRetargetingUpdate.test_retargeting_update.yaml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/cassettes/test_integration_write/TestWriteSitelinks.test_add_delete.yaml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/cassettes/test_integration_write/TestWriteSmartAdTargets.test_add_update_delete.yaml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/cassettes/test_integration_write/TestWriteStrategies.test_strategies_lifecycle.yaml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/cassettes/test_integration_write/TestWriteVCards.test_add_delete.yaml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/cassettes/test_v5_live_write/test_v5_live_draft_adgroups_add_update_delete.yaml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/cassettes/test_v5_live_write/test_v5_live_draft_adimages_add_get_delete.yaml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/cassettes/test_v5_live_write/test_v5_live_draft_ads_add_update_delete.yaml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/cassettes/test_v5_live_write/test_v5_live_draft_ads_suspend_resume_archive_unarchive.yaml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/cassettes/test_v5_live_write/test_v5_live_draft_advideos_add_get.yaml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/cassettes/test_v5_live_write/test_v5_live_draft_audiencetargets_add_delete.yaml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/cassettes/test_v5_live_write/test_v5_live_draft_audiencetargets_suspend_resume.yaml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/cassettes/test_v5_live_write/test_v5_live_draft_bids_set.yaml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/cassettes/test_v5_live_write/test_v5_live_draft_campaign_create_get_delete.yaml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/cassettes/test_v5_live_write/test_v5_live_draft_creatives_chain_advideo_to_creative.yaml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/cassettes/test_v5_live_write/test_v5_live_draft_dynamicads_add_delete.yaml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/cassettes/test_v5_live_write/test_v5_live_draft_dynamicads_suspend_resume.yaml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/cassettes/test_v5_live_write/test_v5_live_draft_keywordbids_set.yaml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/cassettes/test_v5_live_write/test_v5_live_draft_keywords_add_update_delete.yaml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/cassettes/test_v5_live_write/test_v5_live_draft_keywords_suspend_resume.yaml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/cassettes/test_v5_live_write/test_v5_live_draft_sitelinks_add_get_delete.yaml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/cassettes/test_v5_live_write/test_v5_live_draft_smartadtargets_add_update_delete.yaml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/cassettes/test_v5_live_write/test_v5_live_draft_smartadtargets_suspend_resume.yaml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/fixtures/test-video.mp4 +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/reports_cache/raw/fields-list.html +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/reports_cache/raw/headers.html +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/reports_cache/raw/period.html +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/reports_cache/raw/spec.html +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/reports_cache/raw/type.html +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/reports_cache/spec.json +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/test_api_coverage.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/test_auth_bw.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/test_auth_oauth.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/test_auth_op.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/test_auth_write_json.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/test_balance.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/test_changes.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/test_cli.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/test_cli_contract.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/test_comprehensive.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/test_dry_run.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/test_env_loading.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/test_integration.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/test_integration_write.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/test_low_coverage_payloads.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/test_reports_drift.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/test_reports_parsing.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/test_sandbox_write_audit.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/test_smoke_matrix.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/test_transport_contract.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/test_unknown_option_hints.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/test_v4_exit_codes.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/test_v4_foundation.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/test_v4_live_contracts.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/test_v4_safety.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/test_v4account.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/test_v4finance_read.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/test_v4goals.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/test_v4tags.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/test_v4wordstat.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/test_v5_live_write.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/test_vendor_imports.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/test_wsdl_parity_gate.py +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/wsdl_cache/adextensions.xml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/wsdl_cache/adgroups.xml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/wsdl_cache/adimages.xml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/wsdl_cache/ads.xml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/wsdl_cache/advideos.xml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/wsdl_cache/agencyclients.xml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/wsdl_cache/audiencetargets.xml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/wsdl_cache/bidmodifiers.xml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/wsdl_cache/bids.xml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/wsdl_cache/businesses.xml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/wsdl_cache/campaigns.xml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/wsdl_cache/changes.xml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/wsdl_cache/clients.xml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/wsdl_cache/creatives.xml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/wsdl_cache/dictionaries.xml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/wsdl_cache/dynamicfeedadtargets.xml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/wsdl_cache/dynamictextadtargets.xml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/wsdl_cache/feeds.xml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/wsdl_cache/imports/adextensiontypes.xsd +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/wsdl_cache/imports/general.xsd +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/wsdl_cache/imports/generalclients.xsd +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/wsdl_cache/keywordbids.xml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/wsdl_cache/keywords.xml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/wsdl_cache/keywordsresearch.xml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/wsdl_cache/leads.xml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/wsdl_cache/negativekeywordsharedsets.xml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/wsdl_cache/retargetinglists.xml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/wsdl_cache/sitelinks.xml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/wsdl_cache/smartadtargets.xml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/wsdl_cache/strategies.xml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/wsdl_cache/turbopages.xml +0 -0
- {direct_cli-0.3.14 → direct_cli-0.3.16}/tests/wsdl_cache/vcards.xml +0 -0
|
@@ -46,5 +46,5 @@ jobs:
|
|
|
46
46
|
# Optional: Add claude_args to customize behavior and configuration
|
|
47
47
|
# See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md
|
|
48
48
|
# or https://code.claude.com/docs/en/cli-reference for available options
|
|
49
|
-
claude_args: "--model claude-opus-4-
|
|
49
|
+
claude_args: "--model claude-opus-4-8"
|
|
50
50
|
|
|
@@ -1,5 +1,60 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.16
|
|
4
|
+
|
|
5
|
+
**BREAKING CHANGES (regression fix — reverts 0.3.15 wire-shape changes):**
|
|
6
|
+
|
|
7
|
+
- `direct v4finance transfer-money` now requires `--currency` again and
|
|
8
|
+
re-emits `Currency` on every `FromCampaigns` / `ToCampaigns` item.
|
|
9
|
+
The 0.3.15 removal verified against `dg-v4/reference/TransferMoney`
|
|
10
|
+
(legacy v4); the actual Live 4 docs at
|
|
11
|
+
`dg-v4/live/TransferMoney` define `PayCampElement` with
|
|
12
|
+
`CampaignID`, `Sum`, and `Currency`, and explicitly mark `Currency` as
|
|
13
|
+
obligatory in the Live 4 changelog. The CLI now matches the live
|
|
14
|
+
docs 1:1. See audit comment on #125 for the reproducible diff.
|
|
15
|
+
- `direct v4finance pay-campaigns` now requires `--currency` again and
|
|
16
|
+
re-emits `Currency` on every `Payments[]` item. Same root cause:
|
|
17
|
+
`dg-v4/reference/PayCampaigns` (legacy) lacks `Currency`,
|
|
18
|
+
`dg-v4/live/PayCampaigns` (Live 4) requires it.
|
|
19
|
+
- `direct v4finance pay-campaigns` accepts `--pay-method Overdraft`
|
|
20
|
+
again. The Live 4 changelog explicitly adds `Overdraft` for direct
|
|
21
|
+
advertisers (paired with `Bank` for agencies). Only `Bank` keeps the
|
|
22
|
+
`--contract-id` requirement.
|
|
23
|
+
- `direct v4finance create-invoice` now requires `--currency` again and
|
|
24
|
+
re-emits `Currency` on every `Payments[]` item, mirroring
|
|
25
|
+
`dg-v4/live/CreateInvoice`.
|
|
26
|
+
|
|
27
|
+
This release reverts the wire-shape changes shipped by PRs #441, #442,
|
|
28
|
+
#443 (which closed #432, #433, #434). The CLI lives in the `v4finance`
|
|
29
|
+
Live group and must mirror `dg-v4/live/*`, not `dg-v4/reference/*`.
|
|
30
|
+
|
|
31
|
+
## 0.3.15
|
|
32
|
+
|
|
33
|
+
**BREAKING CHANGES:**
|
|
34
|
+
|
|
35
|
+
- `direct v4finance transfer-money` no longer accepts `--currency`, and
|
|
36
|
+
the wire-body no longer carries `Currency` on `FromCampaigns`/
|
|
37
|
+
`ToCampaigns` items. The official v4 docs
|
|
38
|
+
(`dg-v4/reference/TransferMoney`) define `PayCampElement` with only
|
|
39
|
+
`CampaignID` and `Sum`; `Sum` is in conventional units. The CLI now
|
|
40
|
+
matches the docs 1:1. Closes #432.
|
|
41
|
+
- `direct v4finance pay-campaigns` no longer accepts `--currency`. The
|
|
42
|
+
v4 documentation (`dg-v4/reference/PayCampaigns`) defines
|
|
43
|
+
`PayCampElement` with only `CampaignID` and `Sum` — `Currency` is not
|
|
44
|
+
part of the wire-body and was never forwarded to the API. The option
|
|
45
|
+
is removed entirely to make the CLI surface 1:1 with the docs.
|
|
46
|
+
- `direct v4finance pay-campaigns` no longer accepts `--pay-method
|
|
47
|
+
Overdraft`. The v4 documentation
|
|
48
|
+
(`dg-v4/reference/PayCampaigns#PayMethod`) lists only `"Bank"` as a
|
|
49
|
+
supported value; `Overdraft` was a historical undocumented value
|
|
50
|
+
retained by the CLI for sandbox flow. Strict 1:1 docs alignment
|
|
51
|
+
drops it.
|
|
52
|
+
- `direct v4finance create-invoice` no longer accepts `--currency`. The
|
|
53
|
+
v4 documentation (`dg-v4/reference/CreateInvoice`) defines
|
|
54
|
+
`PayCampElement` with only `CampaignID` and `Sum` — `Currency` is not
|
|
55
|
+
part of the wire-body and was never forwarded to the API. The option
|
|
56
|
+
is removed entirely to make the CLI surface 1:1 with the docs.
|
|
57
|
+
|
|
3
58
|
## 0.3.14
|
|
4
59
|
|
|
5
60
|
**Fixed:**
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: direct-cli
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.16
|
|
4
4
|
Summary: Command-line interface for Yandex Direct API
|
|
5
5
|
Author: axisrow
|
|
6
6
|
License: MIT
|
|
@@ -154,6 +154,12 @@ direct balance --logins client-login --dry-run
|
|
|
154
154
|
| `--login` | Direct client login |
|
|
155
155
|
| `--profile` | Credential profile name |
|
|
156
156
|
| `--sandbox` | Use sandbox API |
|
|
157
|
+
| `--locale` | Help/message language: `ru` (default) or `en`. Also set via `YANDEX_DIRECT_CLI_LOCALE`. Command and flag names are unchanged. |
|
|
158
|
+
|
|
159
|
+
Help text is shown in Russian by default. Switch to English with
|
|
160
|
+
`direct --locale en ...` or `export YANDEX_DIRECT_CLI_LOCALE=en`. For example,
|
|
161
|
+
`direct v4finance --help` shows the financial master-token setup steps using
|
|
162
|
+
the locale-appropriate Yandex Direct UI labels.
|
|
157
163
|
|
|
158
164
|
### V4 Live Goals
|
|
159
165
|
|
|
@@ -186,6 +192,7 @@ direct v4tags update-banners --banner-ids 2571700 --clear-tags --dry-run
|
|
|
186
192
|
```bash
|
|
187
193
|
direct v4events get-events-log --from 2026-04-14T00:00:00 --to 2026-04-15T00:00:00
|
|
188
194
|
direct v4events get-events-log --from 2026-04-14T00:00:00 --to 2026-04-15T00:00:00 --currency RUB --limit 100 --offset 0 --format table
|
|
195
|
+
direct v4events get-events-log --from 2026-04-14T00:00:00 --to 2026-04-15T00:00:00 --last-event-only Yes --with-text-description Yes --filter-campaign-ids 123,456 --filter-event-type MoneyOut,MoneyIn
|
|
189
196
|
```
|
|
190
197
|
|
|
191
198
|
### V4 Live Wordstat Reports
|
|
@@ -209,6 +216,7 @@ the forecast is ready.
|
|
|
209
216
|
|
|
210
217
|
```bash
|
|
211
218
|
direct v4forecast create --phrases "buy laptop,buy desktop" --geo-ids 213 --currency RUB
|
|
219
|
+
direct v4forecast create --phrases "buy laptop" --geo-ids 213 --auction-bids Yes --common-minus-words "used,broken"
|
|
212
220
|
direct v4forecast list --format table
|
|
213
221
|
direct v4forecast get --forecast-id 123 --format table
|
|
214
222
|
direct v4forecast delete --forecast-id 123
|
|
@@ -235,7 +243,7 @@ direct v4finance get-credit-limits --master-token MASTER_TOKEN --operation-num 1
|
|
|
235
243
|
direct v4finance create-invoice --payment 123=100.50 --payment 456=25 --currency RUB --master-token MASTER_TOKEN --operation-num 124 --finance-login agency-login --dry-run
|
|
236
244
|
direct v4finance check-payment --custom-transaction-id A123456789012345678901234567890B
|
|
237
245
|
direct v4finance transfer-money --from-campaign-id 123 --to-campaign-id 456 --amount 100.50 --currency RUB --master-token MASTER_TOKEN --operation-num 123 --finance-login agency-login --dry-run
|
|
238
|
-
direct v4finance pay-campaigns --campaign-ids 123,456 --amount 100.50 --
|
|
246
|
+
direct v4finance pay-campaigns --campaign-ids 123,456 --amount 100.50 --contract-id CONTRACT_ID --pay-method Bank --currency RUB --master-token MASTER_TOKEN --operation-num 123 --finance-login agency-login --dry-run
|
|
239
247
|
```
|
|
240
248
|
|
|
241
249
|
### V4 Live Shared Account
|
|
@@ -111,6 +111,12 @@ direct balance --logins client-login --dry-run
|
|
|
111
111
|
| `--login` | Direct client login |
|
|
112
112
|
| `--profile` | Credential profile name |
|
|
113
113
|
| `--sandbox` | Use sandbox API |
|
|
114
|
+
| `--locale` | Help/message language: `ru` (default) or `en`. Also set via `YANDEX_DIRECT_CLI_LOCALE`. Command and flag names are unchanged. |
|
|
115
|
+
|
|
116
|
+
Help text is shown in Russian by default. Switch to English with
|
|
117
|
+
`direct --locale en ...` or `export YANDEX_DIRECT_CLI_LOCALE=en`. For example,
|
|
118
|
+
`direct v4finance --help` shows the financial master-token setup steps using
|
|
119
|
+
the locale-appropriate Yandex Direct UI labels.
|
|
114
120
|
|
|
115
121
|
### V4 Live Goals
|
|
116
122
|
|
|
@@ -143,6 +149,7 @@ direct v4tags update-banners --banner-ids 2571700 --clear-tags --dry-run
|
|
|
143
149
|
```bash
|
|
144
150
|
direct v4events get-events-log --from 2026-04-14T00:00:00 --to 2026-04-15T00:00:00
|
|
145
151
|
direct v4events get-events-log --from 2026-04-14T00:00:00 --to 2026-04-15T00:00:00 --currency RUB --limit 100 --offset 0 --format table
|
|
152
|
+
direct v4events get-events-log --from 2026-04-14T00:00:00 --to 2026-04-15T00:00:00 --last-event-only Yes --with-text-description Yes --filter-campaign-ids 123,456 --filter-event-type MoneyOut,MoneyIn
|
|
146
153
|
```
|
|
147
154
|
|
|
148
155
|
### V4 Live Wordstat Reports
|
|
@@ -166,6 +173,7 @@ the forecast is ready.
|
|
|
166
173
|
|
|
167
174
|
```bash
|
|
168
175
|
direct v4forecast create --phrases "buy laptop,buy desktop" --geo-ids 213 --currency RUB
|
|
176
|
+
direct v4forecast create --phrases "buy laptop" --geo-ids 213 --auction-bids Yes --common-minus-words "used,broken"
|
|
169
177
|
direct v4forecast list --format table
|
|
170
178
|
direct v4forecast get --forecast-id 123 --format table
|
|
171
179
|
direct v4forecast delete --forecast-id 123
|
|
@@ -192,7 +200,7 @@ direct v4finance get-credit-limits --master-token MASTER_TOKEN --operation-num 1
|
|
|
192
200
|
direct v4finance create-invoice --payment 123=100.50 --payment 456=25 --currency RUB --master-token MASTER_TOKEN --operation-num 124 --finance-login agency-login --dry-run
|
|
193
201
|
direct v4finance check-payment --custom-transaction-id A123456789012345678901234567890B
|
|
194
202
|
direct v4finance transfer-money --from-campaign-id 123 --to-campaign-id 456 --amount 100.50 --currency RUB --master-token MASTER_TOKEN --operation-num 123 --finance-login agency-login --dry-run
|
|
195
|
-
direct v4finance pay-campaigns --campaign-ids 123,456 --amount 100.50 --
|
|
203
|
+
direct v4finance pay-campaigns --campaign-ids 123,456 --amount 100.50 --contract-id CONTRACT_ID --pay-method Bank --currency RUB --master-token MASTER_TOKEN --operation-num 123 --finance-login agency-login --dry-run
|
|
196
204
|
```
|
|
197
205
|
|
|
198
206
|
### V4 Live Shared Account
|
|
@@ -176,9 +176,15 @@ class V4LiveClientAdapter(JSONAdapterMixin, TapiAdapter):
|
|
|
176
176
|
code = 0
|
|
177
177
|
|
|
178
178
|
if code in (54, 55) and api_params.get("retry_if_exceeded_limit", True):
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
179
|
+
# Bound the limit-exceeded retries with the same attempt budget as
|
|
180
|
+
# the server-error branch below. Without this guard the loop is
|
|
181
|
+
# infinite (sleep 10s, retry, forever) whenever the API keeps
|
|
182
|
+
# returning code 54/55 — which hangs the caller indefinitely.
|
|
183
|
+
if repeat_number < api_params.get("retries_if_server_error", 5):
|
|
184
|
+
logger.warning("v4 Live limit exceeded (code=%s), retry in 10s", code)
|
|
185
|
+
time.sleep(10)
|
|
186
|
+
return True
|
|
187
|
+
return False
|
|
182
188
|
|
|
183
189
|
if code in (52, 1000, 1001, 1002) or response.status_code == 500:
|
|
184
190
|
if repeat_number < api_params.get("retries_if_server_error", 5):
|
|
@@ -8,6 +8,11 @@ from click.core import ParameterSource
|
|
|
8
8
|
|
|
9
9
|
from . import __version__
|
|
10
10
|
from .auth import get_active_profile, get_credentials, load_env_file
|
|
11
|
+
from .i18n import (
|
|
12
|
+
LOCALE_ENV_VAR,
|
|
13
|
+
resolve_locale,
|
|
14
|
+
t,
|
|
15
|
+
)
|
|
11
16
|
from .utils import get_docs_url
|
|
12
17
|
|
|
13
18
|
from .commands.campaigns import campaigns
|
|
@@ -155,6 +160,21 @@ class DirectCliGroup(_NoSuchOptionHintMixin, click.Group):
|
|
|
155
160
|
|
|
156
161
|
command_class = DirectCliCommand
|
|
157
162
|
|
|
163
|
+
def format_epilog(self, ctx, formatter):
|
|
164
|
+
"""Render a locale-aware epilog when the group opts into i18n.
|
|
165
|
+
|
|
166
|
+
A group sets ``localized_epilog_key`` (a key in ``i18n.CATALOG``) and,
|
|
167
|
+
optionally, ``localized_epilog_suffix`` (an already-built tail appended
|
|
168
|
+
verbatim so single-sourced text such as ``V4_EPILOG`` is not
|
|
169
|
+
duplicated). Groups without the attribute keep their static epilog.
|
|
170
|
+
"""
|
|
171
|
+
key = getattr(self, "localized_epilog_key", None)
|
|
172
|
+
if key:
|
|
173
|
+
text = t(key, resolve_locale(ctx))
|
|
174
|
+
suffix = getattr(self, "localized_epilog_suffix", None)
|
|
175
|
+
self.epilog = f"{text}\n\n{suffix}" if suffix else text
|
|
176
|
+
super().format_epilog(ctx, formatter)
|
|
177
|
+
|
|
158
178
|
|
|
159
179
|
def _apply_directcli_classes(command: click.Command) -> None:
|
|
160
180
|
"""Recursively retype *command* (and subcommands) so every node in the
|
|
@@ -182,6 +202,12 @@ def _apply_directcli_classes(command: click.Command) -> None:
|
|
|
182
202
|
@click.option("--login", envvar="YANDEX_DIRECT_LOGIN", help="Client login")
|
|
183
203
|
@click.option("--profile", help="Credential profile name")
|
|
184
204
|
@click.option("--sandbox", is_flag=True, help="Use sandbox API")
|
|
205
|
+
@click.option(
|
|
206
|
+
"--locale",
|
|
207
|
+
envvar=LOCALE_ENV_VAR,
|
|
208
|
+
default=None,
|
|
209
|
+
help="Language for help and messages (ru or en; default: ru)",
|
|
210
|
+
)
|
|
185
211
|
@click.option(
|
|
186
212
|
"--op-token-ref",
|
|
187
213
|
envvar="YANDEX_DIRECT_OP_TOKEN_REF",
|
|
@@ -209,6 +235,7 @@ def cli(
|
|
|
209
235
|
login,
|
|
210
236
|
profile,
|
|
211
237
|
sandbox,
|
|
238
|
+
locale,
|
|
212
239
|
op_token_ref,
|
|
213
240
|
op_login_ref,
|
|
214
241
|
bw_token_ref,
|
|
@@ -218,6 +245,7 @@ def cli(
|
|
|
218
245
|
ctx.ensure_object(dict)
|
|
219
246
|
ctx.obj["sandbox"] = sandbox
|
|
220
247
|
ctx.obj["profile"] = profile
|
|
248
|
+
ctx.obj["locale"] = locale
|
|
221
249
|
active_profile = None
|
|
222
250
|
if ctx.invoked_subcommand != "auth":
|
|
223
251
|
active_profile = get_active_profile()
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
"""Yandex Direct v4 Live events commands."""
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
import click
|
|
8
|
+
|
|
9
|
+
from ..api import create_v4_client
|
|
10
|
+
from ..output import format_output, print_error
|
|
11
|
+
from ..utils import parse_csv_strings, parse_ids
|
|
12
|
+
from ..v4 import build_v4_body, call_v4
|
|
13
|
+
from ..v4_contracts import v4_method_contract
|
|
14
|
+
from .v4shells import V4_EPILOG
|
|
15
|
+
|
|
16
|
+
V4_DATETIME_FORMAT = "%Y-%m-%dT%H:%M:%S"
|
|
17
|
+
V4_DATETIME_RE = re.compile(r"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}")
|
|
18
|
+
|
|
19
|
+
# Documented GetEventsLogFilter.EventType enum (dg-v4/live/GetEventsLog).
|
|
20
|
+
EVENT_TYPES = [
|
|
21
|
+
"BannerModerated",
|
|
22
|
+
"CampaignFinished",
|
|
23
|
+
"LowCTR",
|
|
24
|
+
"MoneyOut",
|
|
25
|
+
"MoneyWarning",
|
|
26
|
+
"MoneyIn",
|
|
27
|
+
"PausedByDayBudget",
|
|
28
|
+
"WarnMinPrice",
|
|
29
|
+
"WarnPlace",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _events_log_filter(
|
|
34
|
+
campaign_ids: Optional[str],
|
|
35
|
+
banner_ids: Optional[str],
|
|
36
|
+
phrase_ids: Optional[str],
|
|
37
|
+
account_ids: Optional[str],
|
|
38
|
+
event_type: Optional[str],
|
|
39
|
+
) -> Optional[dict]:
|
|
40
|
+
"""Build the GetEventsLogFilter object; None when no filter is provided."""
|
|
41
|
+
filter_obj: dict = {}
|
|
42
|
+
for option_name, raw, key in (
|
|
43
|
+
("--filter-campaign-ids", campaign_ids, "CampaignIDS"),
|
|
44
|
+
("--filter-banner-ids", banner_ids, "BannerIDS"),
|
|
45
|
+
("--filter-phrase-ids", phrase_ids, "PhraseIDS"),
|
|
46
|
+
("--filter-account-ids", account_ids, "AccountIDS"),
|
|
47
|
+
):
|
|
48
|
+
if not raw:
|
|
49
|
+
continue
|
|
50
|
+
try:
|
|
51
|
+
parsed = parse_ids(raw)
|
|
52
|
+
except ValueError as exc:
|
|
53
|
+
raise click.UsageError(f"{option_name}: {exc}") from exc
|
|
54
|
+
if parsed:
|
|
55
|
+
filter_obj[key] = parsed
|
|
56
|
+
if event_type:
|
|
57
|
+
types = parse_csv_strings(event_type)
|
|
58
|
+
if types:
|
|
59
|
+
unknown = [value for value in types if value not in EVENT_TYPES]
|
|
60
|
+
if unknown:
|
|
61
|
+
raise click.UsageError(
|
|
62
|
+
"--filter-event-type has unknown values: "
|
|
63
|
+
f"{', '.join(unknown)}. Valid values: {', '.join(EVENT_TYPES)}"
|
|
64
|
+
)
|
|
65
|
+
filter_obj["EventType"] = types
|
|
66
|
+
return filter_obj or None
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _parse_v4_datetime(value: str, option_name: str) -> datetime:
|
|
70
|
+
"""Parse a strict v4 Live CLI datetime token."""
|
|
71
|
+
if not V4_DATETIME_RE.fullmatch(value):
|
|
72
|
+
raise click.UsageError(
|
|
73
|
+
f"Invalid {option_name}: {value}. Expected YYYY-MM-DDTHH:MM:SS"
|
|
74
|
+
)
|
|
75
|
+
try:
|
|
76
|
+
return datetime.strptime(value, V4_DATETIME_FORMAT)
|
|
77
|
+
except ValueError:
|
|
78
|
+
raise click.UsageError(
|
|
79
|
+
f"Invalid {option_name}: {value}. Expected YYYY-MM-DDTHH:MM:SS"
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _events_log_param(
|
|
84
|
+
timestamp_from: str,
|
|
85
|
+
timestamp_to: str,
|
|
86
|
+
currency: str,
|
|
87
|
+
limit: Optional[int],
|
|
88
|
+
offset: Optional[int],
|
|
89
|
+
last_event_only: Optional[str] = None,
|
|
90
|
+
with_text_description: Optional[str] = None,
|
|
91
|
+
logins: Optional[str] = None,
|
|
92
|
+
campaign_ids: Optional[str] = None,
|
|
93
|
+
banner_ids: Optional[str] = None,
|
|
94
|
+
phrase_ids: Optional[str] = None,
|
|
95
|
+
account_ids: Optional[str] = None,
|
|
96
|
+
event_type: Optional[str] = None,
|
|
97
|
+
) -> dict:
|
|
98
|
+
"""Build the v4 Live GetEventsLog parameter."""
|
|
99
|
+
from_dt = _parse_v4_datetime(timestamp_from, "--from")
|
|
100
|
+
to_dt = _parse_v4_datetime(timestamp_to, "--to")
|
|
101
|
+
if from_dt > to_dt:
|
|
102
|
+
raise click.UsageError("--from must be earlier than or equal to --to")
|
|
103
|
+
|
|
104
|
+
param = {
|
|
105
|
+
"TimestampFrom": timestamp_from,
|
|
106
|
+
"TimestampTo": timestamp_to,
|
|
107
|
+
"Currency": currency,
|
|
108
|
+
}
|
|
109
|
+
if last_event_only:
|
|
110
|
+
param["LastEventOnly"] = last_event_only
|
|
111
|
+
if with_text_description:
|
|
112
|
+
param["WithTextDescription"] = with_text_description
|
|
113
|
+
if logins:
|
|
114
|
+
login_list = parse_csv_strings(logins)
|
|
115
|
+
if login_list:
|
|
116
|
+
param["Logins"] = login_list
|
|
117
|
+
filter_obj = _events_log_filter(
|
|
118
|
+
campaign_ids, banner_ids, phrase_ids, account_ids, event_type
|
|
119
|
+
)
|
|
120
|
+
if filter_obj is not None:
|
|
121
|
+
param["Filter"] = filter_obj
|
|
122
|
+
if limit is not None:
|
|
123
|
+
param["Limit"] = limit
|
|
124
|
+
if offset is not None:
|
|
125
|
+
param["Offset"] = offset
|
|
126
|
+
return param
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
@click.group(epilog=V4_EPILOG)
|
|
130
|
+
def v4events():
|
|
131
|
+
"""Yandex Direct v4 Live events commands."""
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
@v4_method_contract("GetEventsLog")
|
|
135
|
+
@v4events.command(name="get-events-log")
|
|
136
|
+
@click.option(
|
|
137
|
+
"--from",
|
|
138
|
+
"timestamp_from",
|
|
139
|
+
required=True,
|
|
140
|
+
help="Start timestamp in YYYY-MM-DDTHH:MM:SS format",
|
|
141
|
+
)
|
|
142
|
+
@click.option(
|
|
143
|
+
"--to",
|
|
144
|
+
"timestamp_to",
|
|
145
|
+
required=True,
|
|
146
|
+
help="End timestamp in YYYY-MM-DDTHH:MM:SS format",
|
|
147
|
+
)
|
|
148
|
+
@click.option(
|
|
149
|
+
"--last-event-only",
|
|
150
|
+
type=click.Choice(["Yes", "No"]),
|
|
151
|
+
help="Return only the latest record per event type — Yes/No",
|
|
152
|
+
)
|
|
153
|
+
@click.option(
|
|
154
|
+
"--with-text-description",
|
|
155
|
+
type=click.Choice(["Yes", "No"]),
|
|
156
|
+
help="Include event text descriptions in the response — Yes/No",
|
|
157
|
+
)
|
|
158
|
+
@click.option("--currency", default="RUB", show_default=True, help="Currency")
|
|
159
|
+
@click.option("--logins", help="Comma-separated client logins")
|
|
160
|
+
@click.option("--filter-campaign-ids", help="Filter: comma-separated campaign IDs")
|
|
161
|
+
@click.option("--filter-banner-ids", help="Filter: comma-separated banner IDs")
|
|
162
|
+
@click.option("--filter-phrase-ids", help="Filter: comma-separated phrase IDs")
|
|
163
|
+
@click.option("--filter-account-ids", help="Filter: comma-separated account IDs")
|
|
164
|
+
@click.option(
|
|
165
|
+
"--filter-event-type",
|
|
166
|
+
help="Filter: comma-separated event types (" + ", ".join(EVENT_TYPES) + ")",
|
|
167
|
+
)
|
|
168
|
+
@click.option("--limit", type=click.IntRange(min=0), help="Result limit")
|
|
169
|
+
@click.option("--offset", type=click.IntRange(min=0), help="Result offset")
|
|
170
|
+
@click.option(
|
|
171
|
+
"--format",
|
|
172
|
+
"output_format",
|
|
173
|
+
default="json",
|
|
174
|
+
type=click.Choice(["json", "table", "csv", "tsv"]),
|
|
175
|
+
help="Output format",
|
|
176
|
+
)
|
|
177
|
+
@click.option("--output", help="Output file")
|
|
178
|
+
@click.option("--dry-run", is_flag=True, help="Show request without sending")
|
|
179
|
+
@click.pass_context
|
|
180
|
+
def get_events_log(
|
|
181
|
+
ctx,
|
|
182
|
+
timestamp_from,
|
|
183
|
+
timestamp_to,
|
|
184
|
+
last_event_only,
|
|
185
|
+
with_text_description,
|
|
186
|
+
currency,
|
|
187
|
+
logins,
|
|
188
|
+
filter_campaign_ids,
|
|
189
|
+
filter_banner_ids,
|
|
190
|
+
filter_phrase_ids,
|
|
191
|
+
filter_account_ids,
|
|
192
|
+
filter_event_type,
|
|
193
|
+
limit,
|
|
194
|
+
offset,
|
|
195
|
+
output_format,
|
|
196
|
+
output,
|
|
197
|
+
dry_run,
|
|
198
|
+
):
|
|
199
|
+
"""Get v4 Live events log entries."""
|
|
200
|
+
param = _events_log_param(
|
|
201
|
+
timestamp_from,
|
|
202
|
+
timestamp_to,
|
|
203
|
+
currency,
|
|
204
|
+
limit,
|
|
205
|
+
offset,
|
|
206
|
+
last_event_only=last_event_only,
|
|
207
|
+
with_text_description=with_text_description,
|
|
208
|
+
logins=logins,
|
|
209
|
+
campaign_ids=filter_campaign_ids,
|
|
210
|
+
banner_ids=filter_banner_ids,
|
|
211
|
+
phrase_ids=filter_phrase_ids,
|
|
212
|
+
account_ids=filter_account_ids,
|
|
213
|
+
event_type=filter_event_type,
|
|
214
|
+
)
|
|
215
|
+
if dry_run:
|
|
216
|
+
format_output(build_v4_body("GetEventsLog", param), "json", None)
|
|
217
|
+
return
|
|
218
|
+
|
|
219
|
+
try:
|
|
220
|
+
client = create_v4_client(
|
|
221
|
+
token=ctx.obj.get("token"),
|
|
222
|
+
login=ctx.obj.get("login"),
|
|
223
|
+
profile=ctx.obj.get("profile"),
|
|
224
|
+
sandbox=ctx.obj.get("sandbox"),
|
|
225
|
+
)
|
|
226
|
+
data = call_v4(client, "GetEventsLog", param)
|
|
227
|
+
format_output(data, output_format, output)
|
|
228
|
+
except click.ClickException:
|
|
229
|
+
raise
|
|
230
|
+
except Exception as e:
|
|
231
|
+
print_error(str(e))
|
|
232
|
+
raise click.Abort()
|
|
@@ -6,6 +6,7 @@ from typing import Any, Optional
|
|
|
6
6
|
import click
|
|
7
7
|
|
|
8
8
|
from ..api import create_v4_client
|
|
9
|
+
from ..i18n import LocalizedOption
|
|
9
10
|
from ..output import format_output, print_error
|
|
10
11
|
from ..utils import parse_csv_strings, parse_ids
|
|
11
12
|
from ..v4 import build_v4_body, call_v4
|
|
@@ -15,8 +16,8 @@ from .v4shells import V4_EPILOG
|
|
|
15
16
|
|
|
16
17
|
FINANCE_TOKEN_MASK = "<redacted>"
|
|
17
18
|
CUSTOM_TRANSACTION_ID_RE = re.compile(r"[A-Za-z0-9]{32}")
|
|
18
|
-
V4_FINANCE_CURRENCIES = ["RUB", "USD", "EUR", "BYN", "KZT", "TRY", "UAH", "CHF"]
|
|
19
19
|
V4_PAY_METHODS = ["Bank", "Overdraft"]
|
|
20
|
+
V4_FINANCE_CURRENCIES = ("RUB", "CHF", "EUR", "KZT", "TRY", "UAH", "USD", "BYN")
|
|
20
21
|
FINANCE_HELP_EPILOG = (
|
|
21
22
|
"To issue a master token in the Yandex Direct UI, open Tools -> API -> "
|
|
22
23
|
"Financial operations, enable the 'Allow financial operations' checkbox, "
|
|
@@ -39,9 +40,9 @@ def _invoice_payments_param(payments: tuple[str, ...], currency: str) -> dict:
|
|
|
39
40
|
if not payments:
|
|
40
41
|
raise click.UsageError("--payment is required")
|
|
41
42
|
|
|
43
|
+
normalized_currency = currency.upper()
|
|
42
44
|
parsed_payments = []
|
|
43
45
|
seen_campaign_ids = set()
|
|
44
|
-
normalized_currency = currency.upper()
|
|
45
46
|
for payment in payments:
|
|
46
47
|
spec = (payment or "").strip()
|
|
47
48
|
if "=" not in spec:
|
|
@@ -159,6 +160,13 @@ def v4finance():
|
|
|
159
160
|
"""Yandex Direct v4 Live finance commands."""
|
|
160
161
|
|
|
161
162
|
|
|
163
|
+
# Localize the group epilog at render time (Russian by default, English via
|
|
164
|
+
# --locale en / YANDEX_DIRECT_CLI_LOCALE). The V4_EPILOG tail is appended
|
|
165
|
+
# verbatim so its single-sourced docs URL is not duplicated. See i18n.py.
|
|
166
|
+
v4finance.localized_epilog_key = "v4finance.master_token_setup"
|
|
167
|
+
v4finance.localized_epilog_suffix = V4_EPILOG
|
|
168
|
+
|
|
169
|
+
|
|
162
170
|
@v4_method_contract("GetClientsUnits")
|
|
163
171
|
@v4finance.command(name="get-clients-units")
|
|
164
172
|
@click.option("--logins", required=True, help="Comma-separated client logins")
|
|
@@ -205,11 +213,9 @@ def get_clients_units(ctx, logins, output_format, output, dry_run):
|
|
|
205
213
|
)
|
|
206
214
|
@click.option(
|
|
207
215
|
"--master-token",
|
|
216
|
+
cls=LocalizedOption,
|
|
217
|
+
help_key="v4finance.master_token_option",
|
|
208
218
|
envvar="YANDEX_DIRECT_MASTER_TOKEN",
|
|
209
|
-
help=(
|
|
210
|
-
"Financial master token issued after enabling and saving financial "
|
|
211
|
-
"operations in Tools -> API -> Financial operations"
|
|
212
|
-
),
|
|
213
219
|
)
|
|
214
220
|
@click.option(
|
|
215
221
|
"--operation-num",
|
|
@@ -335,10 +341,9 @@ def check_payment(
|
|
|
335
341
|
)
|
|
336
342
|
@click.option(
|
|
337
343
|
"--currency",
|
|
338
|
-
|
|
339
|
-
show_default=True,
|
|
344
|
+
required=True,
|
|
340
345
|
type=click.Choice(V4_FINANCE_CURRENCIES, case_sensitive=False),
|
|
341
|
-
help="Payment currency",
|
|
346
|
+
help="Payment currency (RUB/CHF/EUR/KZT/TRY/UAH/USD/BYN)",
|
|
342
347
|
)
|
|
343
348
|
@click.option(
|
|
344
349
|
"--finance-token",
|
|
@@ -347,11 +352,9 @@ def check_payment(
|
|
|
347
352
|
)
|
|
348
353
|
@click.option(
|
|
349
354
|
"--master-token",
|
|
355
|
+
cls=LocalizedOption,
|
|
356
|
+
help_key="v4finance.master_token_option",
|
|
350
357
|
envvar="YANDEX_DIRECT_MASTER_TOKEN",
|
|
351
|
-
help=(
|
|
352
|
-
"Financial master token issued after enabling and saving financial "
|
|
353
|
-
"operations in Tools -> API -> Financial operations"
|
|
354
|
-
),
|
|
355
358
|
)
|
|
356
359
|
@click.option(
|
|
357
360
|
"--operation-num",
|
|
@@ -440,10 +443,9 @@ def create_invoice(
|
|
|
440
443
|
@click.option("--amount", required=True, help="Positive amount, for example 100.50")
|
|
441
444
|
@click.option(
|
|
442
445
|
"--currency",
|
|
443
|
-
|
|
444
|
-
show_default=True,
|
|
446
|
+
required=True,
|
|
445
447
|
type=click.Choice(V4_FINANCE_CURRENCIES, case_sensitive=False),
|
|
446
|
-
help="
|
|
448
|
+
help="Transfer currency (RUB/CHF/EUR/KZT/TRY/UAH/USD/BYN)",
|
|
447
449
|
)
|
|
448
450
|
@click.option(
|
|
449
451
|
"--finance-token",
|
|
@@ -452,11 +454,9 @@ def create_invoice(
|
|
|
452
454
|
)
|
|
453
455
|
@click.option(
|
|
454
456
|
"--master-token",
|
|
457
|
+
cls=LocalizedOption,
|
|
458
|
+
help_key="v4finance.master_token_option",
|
|
455
459
|
envvar="YANDEX_DIRECT_MASTER_TOKEN",
|
|
456
|
-
help=(
|
|
457
|
-
"Financial master token issued after enabling and saving financial "
|
|
458
|
-
"operations in Tools -> API -> Financial operations"
|
|
459
|
-
),
|
|
460
460
|
)
|
|
461
461
|
@click.option(
|
|
462
462
|
"--operation-num",
|
|
@@ -498,20 +498,20 @@ def transfer_money(
|
|
|
498
498
|
ctx.obj.get("login"),
|
|
499
499
|
)
|
|
500
500
|
parsed_amount = parse_v4_money_sum(amount)
|
|
501
|
-
|
|
501
|
+
normalized_currency = currency.upper()
|
|
502
502
|
param = {
|
|
503
503
|
"FromCampaigns": [
|
|
504
504
|
{
|
|
505
505
|
"CampaignID": from_campaign_id,
|
|
506
506
|
"Sum": parsed_amount,
|
|
507
|
-
"Currency":
|
|
507
|
+
"Currency": normalized_currency,
|
|
508
508
|
},
|
|
509
509
|
],
|
|
510
510
|
"ToCampaigns": [
|
|
511
511
|
{
|
|
512
512
|
"CampaignID": to_campaign_id,
|
|
513
513
|
"Sum": parsed_amount,
|
|
514
|
-
"Currency":
|
|
514
|
+
"Currency": normalized_currency,
|
|
515
515
|
},
|
|
516
516
|
],
|
|
517
517
|
}
|
|
@@ -531,19 +531,18 @@ def transfer_money(
|
|
|
531
531
|
help="Comma-separated campaign IDs to pay",
|
|
532
532
|
)
|
|
533
533
|
@click.option("--amount", required=True, help="Positive amount, for example 100.50")
|
|
534
|
-
@click.option(
|
|
535
|
-
"--currency",
|
|
536
|
-
default="RUB",
|
|
537
|
-
show_default=True,
|
|
538
|
-
type=click.Choice(V4_FINANCE_CURRENCIES, case_sensitive=False),
|
|
539
|
-
help="Payment currency",
|
|
540
|
-
)
|
|
541
534
|
@click.option("--contract-id", help="Agency contract ID; required for Bank")
|
|
542
535
|
@click.option(
|
|
543
536
|
"--pay-method",
|
|
544
537
|
required=True,
|
|
545
538
|
type=click.Choice(V4_PAY_METHODS, case_sensitive=False),
|
|
546
|
-
help="Payment method",
|
|
539
|
+
help="Payment method: Bank (agency credit) or Overdraft (direct advertiser)",
|
|
540
|
+
)
|
|
541
|
+
@click.option(
|
|
542
|
+
"--currency",
|
|
543
|
+
required=True,
|
|
544
|
+
type=click.Choice(V4_FINANCE_CURRENCIES, case_sensitive=False),
|
|
545
|
+
help="Payment currency (RUB/CHF/EUR/KZT/TRY/UAH/USD/BYN)",
|
|
547
546
|
)
|
|
548
547
|
@click.option(
|
|
549
548
|
"--finance-token",
|
|
@@ -552,11 +551,9 @@ def transfer_money(
|
|
|
552
551
|
)
|
|
553
552
|
@click.option(
|
|
554
553
|
"--master-token",
|
|
554
|
+
cls=LocalizedOption,
|
|
555
|
+
help_key="v4finance.master_token_option",
|
|
555
556
|
envvar="YANDEX_DIRECT_MASTER_TOKEN",
|
|
556
|
-
help=(
|
|
557
|
-
"Financial master token issued after enabling and saving financial "
|
|
558
|
-
"operations in Tools -> API -> Financial operations"
|
|
559
|
-
),
|
|
560
557
|
)
|
|
561
558
|
@click.option(
|
|
562
559
|
"--operation-num",
|
|
@@ -579,9 +576,9 @@ def pay_campaigns(
|
|
|
579
576
|
ctx,
|
|
580
577
|
campaign_ids,
|
|
581
578
|
amount,
|
|
582
|
-
currency,
|
|
583
579
|
contract_id,
|
|
584
580
|
pay_method,
|
|
581
|
+
currency,
|
|
585
582
|
finance_token,
|
|
586
583
|
master_token,
|
|
587
584
|
operation_num,
|
|
@@ -600,14 +597,18 @@ def pay_campaigns(
|
|
|
600
597
|
)
|
|
601
598
|
parsed_amount = parse_v4_money_sum(amount)
|
|
602
599
|
parsed_campaign_ids = _campaign_ids_param(campaign_ids)
|
|
603
|
-
currency = currency.upper()
|
|
604
600
|
pay_method = _non_empty_option(pay_method, "--pay-method")
|
|
601
|
+
normalized_currency = currency.upper()
|
|
605
602
|
contract_id = (contract_id or "").strip()
|
|
606
603
|
if pay_method == "Bank" and not contract_id:
|
|
607
604
|
raise click.UsageError("--contract-id is required when --pay-method Bank")
|
|
608
605
|
param = {
|
|
609
606
|
"Payments": [
|
|
610
|
-
{
|
|
607
|
+
{
|
|
608
|
+
"CampaignID": campaign_id,
|
|
609
|
+
"Sum": parsed_amount,
|
|
610
|
+
"Currency": normalized_currency,
|
|
611
|
+
}
|
|
611
612
|
for campaign_id in parsed_campaign_ids
|
|
612
613
|
],
|
|
613
614
|
"PayMethod": pay_method,
|