direct-cli 0.3.1__tar.gz → 0.3.2__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.
Files changed (207) hide show
  1. {direct_cli-0.3.1 → direct_cli-0.3.2}/PKG-INFO +42 -7
  2. {direct_cli-0.3.1 → direct_cli-0.3.2}/README.md +41 -6
  3. {direct_cli-0.3.1 → direct_cli-0.3.2}/direct_cli/_vendor/tapi_yandex_direct/v4/adapter.py +12 -5
  4. {direct_cli-0.3.1 → direct_cli-0.3.2}/direct_cli/_vendor/tapi_yandex_direct/v4/adapter.pyi +2 -0
  5. {direct_cli-0.3.1 → direct_cli-0.3.2}/direct_cli/api.py +6 -0
  6. {direct_cli-0.3.1 → direct_cli-0.3.2}/direct_cli/cli.py +3 -3
  7. {direct_cli-0.3.1 → direct_cli-0.3.2}/direct_cli/commands/adextensions.py +27 -2
  8. {direct_cli-0.3.1 → direct_cli-0.3.2}/direct_cli/commands/adgroups.py +36 -1
  9. {direct_cli-0.3.1 → direct_cli-0.3.2}/direct_cli/commands/adimages.py +27 -6
  10. {direct_cli-0.3.1 → direct_cli-0.3.2}/direct_cli/commands/ads.py +55 -4
  11. {direct_cli-0.3.1 → direct_cli-0.3.2}/direct_cli/commands/advideos.py +11 -6
  12. {direct_cli-0.3.1 → direct_cli-0.3.2}/direct_cli/commands/audiencetargets.py +18 -1
  13. {direct_cli-0.3.1 → direct_cli-0.3.2}/direct_cli/commands/bidmodifiers.py +16 -0
  14. {direct_cli-0.3.1 → direct_cli-0.3.2}/direct_cli/commands/bids.py +10 -1
  15. {direct_cli-0.3.1 → direct_cli-0.3.2}/direct_cli/commands/businesses.py +9 -2
  16. {direct_cli-0.3.1 → direct_cli-0.3.2}/direct_cli/commands/campaigns.py +40 -20
  17. {direct_cli-0.3.1 → direct_cli-0.3.2}/direct_cli/commands/creatives.py +11 -1
  18. {direct_cli-0.3.1 → direct_cli-0.3.2}/direct_cli/commands/dynamicads.py +26 -1
  19. {direct_cli-0.3.1 → direct_cli-0.3.2}/direct_cli/commands/dynamicfeedadtargets.py +22 -2
  20. {direct_cli-0.3.1 → direct_cli-0.3.2}/direct_cli/commands/feeds.py +9 -2
  21. {direct_cli-0.3.1 → direct_cli-0.3.2}/direct_cli/commands/keywordbids.py +18 -2
  22. {direct_cli-0.3.1 → direct_cli-0.3.2}/direct_cli/commands/keywords.py +50 -13
  23. {direct_cli-0.3.1 → direct_cli-0.3.2}/direct_cli/commands/leads.py +23 -1
  24. {direct_cli-0.3.1 → direct_cli-0.3.2}/direct_cli/commands/negativekeywordsharedsets.py +10 -5
  25. {direct_cli-0.3.1 → direct_cli-0.3.2}/direct_cli/commands/reports.py +124 -22
  26. {direct_cli-0.3.1 → direct_cli-0.3.2}/direct_cli/commands/sitelinks.py +9 -2
  27. {direct_cli-0.3.1 → direct_cli-0.3.2}/direct_cli/commands/smartadtargets.py +26 -1
  28. {direct_cli-0.3.1 → direct_cli-0.3.2}/direct_cli/commands/turbopages.py +16 -2
  29. direct_cli-0.3.2/direct_cli/commands/v4account.py +218 -0
  30. direct_cli-0.3.2/direct_cli/commands/v4events.py +118 -0
  31. direct_cli-0.3.2/direct_cli/commands/v4finance.py +298 -0
  32. {direct_cli-0.3.1 → direct_cli-0.3.2}/direct_cli/commands/v4shells.py +0 -15
  33. {direct_cli-0.3.1 → direct_cli-0.3.2}/direct_cli/commands/vcards.py +13 -3
  34. {direct_cli-0.3.1 → direct_cli-0.3.2}/direct_cli/reports_coverage.py +113 -15
  35. {direct_cli-0.3.1 → direct_cli-0.3.2}/direct_cli/smoke_matrix.py +7 -0
  36. {direct_cli-0.3.1 → direct_cli-0.3.2}/direct_cli/utils.py +25 -0
  37. direct_cli-0.3.2/direct_cli/v4/money.py +35 -0
  38. {direct_cli-0.3.1 → direct_cli-0.3.2}/direct_cli/v4_contracts.py +83 -22
  39. {direct_cli-0.3.1 → direct_cli-0.3.2}/direct_cli.egg-info/PKG-INFO +42 -7
  40. {direct_cli-0.3.1 → direct_cli-0.3.2}/direct_cli.egg-info/SOURCES.txt +11 -1
  41. {direct_cli-0.3.1 → direct_cli-0.3.2}/pyproject.toml +2 -2
  42. {direct_cli-0.3.1 → direct_cli-0.3.2}/scripts/build_api_coverage_report.py +1 -0
  43. {direct_cli-0.3.1 → direct_cli-0.3.2}/scripts/test_dangerous_commands.sh +4 -0
  44. {direct_cli-0.3.1 → direct_cli-0.3.2}/scripts/test_safe_commands.sh +30 -0
  45. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/API_COVERAGE.md +22 -2
  46. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/MANUAL_COVERAGE.md +12 -0
  47. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/api_coverage_payloads.py +28 -0
  48. direct_cli-0.3.1/tests/reports_cache/raw/spec.html → direct_cli-0.3.2/tests/reports_cache/raw/period.html +9 -9
  49. direct_cli-0.3.2/tests/reports_cache/raw/spec.html +660 -0
  50. direct_cli-0.3.2/tests/reports_cache/spec.json +1645 -0
  51. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/test_api_coverage.py +210 -19
  52. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/test_dry_run.py +316 -0
  53. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/test_smoke_matrix.py +6 -2
  54. direct_cli-0.3.2/tests/test_v4_contracts.py +292 -0
  55. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/test_v4_foundation.py +4 -0
  56. direct_cli-0.3.2/tests/test_v4_live_contracts.py +154 -0
  57. direct_cli-0.3.2/tests/test_v4_safety.py +15 -0
  58. direct_cli-0.3.2/tests/test_v4account.py +304 -0
  59. direct_cli-0.3.2/tests/test_v4events.py +162 -0
  60. direct_cli-0.3.2/tests/test_v4finance_money.py +320 -0
  61. direct_cli-0.3.2/tests/test_v4finance_read.py +202 -0
  62. direct_cli-0.3.1/tests/reports_cache/spec.json +0 -583
  63. direct_cli-0.3.1/tests/test_v4_contracts.py +0 -141
  64. direct_cli-0.3.1/tests/test_v4_live_contracts.py +0 -79
  65. {direct_cli-0.3.1 → direct_cli-0.3.2}/.env.example +0 -0
  66. {direct_cli-0.3.1 → direct_cli-0.3.2}/.github/copilot-instructions.md +0 -0
  67. {direct_cli-0.3.1 → direct_cli-0.3.2}/.github/workflows/api-coverage.yml +0 -0
  68. {direct_cli-0.3.1 → direct_cli-0.3.2}/.github/workflows/claude-code-review.yml +0 -0
  69. {direct_cli-0.3.1 → direct_cli-0.3.2}/.github/workflows/claude.yml +0 -0
  70. {direct_cli-0.3.1 → direct_cli-0.3.2}/.gitignore +0 -0
  71. {direct_cli-0.3.1 → direct_cli-0.3.2}/AGENTS.md +0 -0
  72. {direct_cli-0.3.1 → direct_cli-0.3.2}/CLAUDE.md +0 -0
  73. {direct_cli-0.3.1 → direct_cli-0.3.2}/MANIFEST.in +0 -0
  74. {direct_cli-0.3.1 → direct_cli-0.3.2}/direct_cli/__init__.py +0 -0
  75. {direct_cli-0.3.1 → direct_cli-0.3.2}/direct_cli/_deprecated.py +0 -0
  76. {direct_cli-0.3.1 → direct_cli-0.3.2}/direct_cli/_smoke_probes.py +0 -0
  77. {direct_cli-0.3.1 → direct_cli-0.3.2}/direct_cli/_vendor/__init__.py +0 -0
  78. {direct_cli-0.3.1 → direct_cli-0.3.2}/direct_cli/_vendor/tapi_yandex_direct/__init__.py +0 -0
  79. {direct_cli-0.3.1 → direct_cli-0.3.2}/direct_cli/_vendor/tapi_yandex_direct/exceptions.py +0 -0
  80. {direct_cli-0.3.1 → direct_cli-0.3.2}/direct_cli/_vendor/tapi_yandex_direct/resource_mapping.py +0 -0
  81. {direct_cli-0.3.1 → direct_cli-0.3.2}/direct_cli/_vendor/tapi_yandex_direct/tapi_yandex_direct.py +0 -0
  82. {direct_cli-0.3.1 → direct_cli-0.3.2}/direct_cli/_vendor/tapi_yandex_direct/tapi_yandex_direct.pyi +0 -0
  83. {direct_cli-0.3.1 → direct_cli-0.3.2}/direct_cli/_vendor/tapi_yandex_direct/v4/__init__.py +0 -0
  84. {direct_cli-0.3.1 → direct_cli-0.3.2}/direct_cli/_vendor/tapi_yandex_direct/v4/resource_mapping.py +0 -0
  85. {direct_cli-0.3.1 → direct_cli-0.3.2}/direct_cli/auth.py +0 -0
  86. {direct_cli-0.3.1 → direct_cli-0.3.2}/direct_cli/commands/__init__.py +0 -0
  87. {direct_cli-0.3.1 → direct_cli-0.3.2}/direct_cli/commands/agencyclients.py +0 -0
  88. {direct_cli-0.3.1 → direct_cli-0.3.2}/direct_cli/commands/auth.py +0 -0
  89. {direct_cli-0.3.1 → direct_cli-0.3.2}/direct_cli/commands/balance.py +0 -0
  90. {direct_cli-0.3.1 → direct_cli-0.3.2}/direct_cli/commands/changes.py +0 -0
  91. {direct_cli-0.3.1 → direct_cli-0.3.2}/direct_cli/commands/clients.py +0 -0
  92. {direct_cli-0.3.1 → direct_cli-0.3.2}/direct_cli/commands/dictionaries.py +0 -0
  93. {direct_cli-0.3.1 → direct_cli-0.3.2}/direct_cli/commands/keywordsresearch.py +0 -0
  94. {direct_cli-0.3.1 → direct_cli-0.3.2}/direct_cli/commands/retargeting.py +0 -0
  95. {direct_cli-0.3.1 → direct_cli-0.3.2}/direct_cli/commands/strategies.py +0 -0
  96. {direct_cli-0.3.1 → direct_cli-0.3.2}/direct_cli/commands/v4goals.py +0 -0
  97. {direct_cli-0.3.1 → direct_cli-0.3.2}/direct_cli/output.py +0 -0
  98. /direct_cli-0.3.1/direct_cli/v4.py → /direct_cli-0.3.2/direct_cli/v4/__init__.py +0 -0
  99. {direct_cli-0.3.1 → direct_cli-0.3.2}/direct_cli/wsdl_coverage.py +0 -0
  100. {direct_cli-0.3.1 → direct_cli-0.3.2}/direct_cli.egg-info/dependency_links.txt +0 -0
  101. {direct_cli-0.3.1 → direct_cli-0.3.2}/direct_cli.egg-info/entry_points.txt +0 -0
  102. {direct_cli-0.3.1 → direct_cli-0.3.2}/direct_cli.egg-info/requires.txt +0 -0
  103. {direct_cli-0.3.1 → direct_cli-0.3.2}/direct_cli.egg-info/top_level.txt +0 -0
  104. {direct_cli-0.3.1 → direct_cli-0.3.2}/docs/superpowers/plans/2026-04-12-issue-32-completion.md +0 -0
  105. {direct_cli-0.3.1 → direct_cli-0.3.2}/docs/superpowers/specs/2026-04-23-vendor-tapi-yandex-direct-design.md +0 -0
  106. {direct_cli-0.3.1 → direct_cli-0.3.2}/scripts/anonymize_cassettes.py +0 -0
  107. {direct_cli-0.3.1 → direct_cli-0.3.2}/scripts/build_api_coverage_checklist.py +0 -0
  108. {direct_cli-0.3.1 → direct_cli-0.3.2}/scripts/check_reports_drift.py +0 -0
  109. {direct_cli-0.3.1 → direct_cli-0.3.2}/scripts/check_wsdl_drift.py +0 -0
  110. {direct_cli-0.3.1 → direct_cli-0.3.2}/scripts/patch_vendor_imports.py +0 -0
  111. {direct_cli-0.3.1 → direct_cli-0.3.2}/scripts/refresh_reports_cache.py +0 -0
  112. {direct_cli-0.3.1 → direct_cli-0.3.2}/scripts/refresh_wsdl_cache.py +0 -0
  113. {direct_cli-0.3.1 → direct_cli-0.3.2}/scripts/release_pypi.sh +0 -0
  114. {direct_cli-0.3.1 → direct_cli-0.3.2}/scripts/sandbox_write_live.py +0 -0
  115. {direct_cli-0.3.1 → direct_cli-0.3.2}/scripts/test_sandbox_write.sh +0 -0
  116. {direct_cli-0.3.1 → direct_cli-0.3.2}/scripts/update_vendor.sh +0 -0
  117. {direct_cli-0.3.1 → direct_cli-0.3.2}/setup.cfg +0 -0
  118. {direct_cli-0.3.1 → direct_cli-0.3.2}/setup.py +0 -0
  119. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/API_ISSUE_AUDIT.md +0 -0
  120. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/__init__.py +0 -0
  121. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/cassettes/test_integration_live_write/test_live_draft_adgroups_add_update_delete.yaml +0 -0
  122. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/cassettes/test_integration_live_write/test_live_draft_adimages_add_get_delete.yaml +0 -0
  123. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/cassettes/test_integration_live_write/test_live_draft_ads_add_update_delete.yaml +0 -0
  124. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/cassettes/test_integration_live_write/test_live_draft_ads_suspend_resume_archive_unarchive.yaml +0 -0
  125. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/cassettes/test_integration_live_write/test_live_draft_advideos_add_get.yaml +0 -0
  126. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/cassettes/test_integration_live_write/test_live_draft_audiencetargets_add_delete.yaml +0 -0
  127. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/cassettes/test_integration_live_write/test_live_draft_audiencetargets_suspend_resume.yaml +0 -0
  128. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/cassettes/test_integration_live_write/test_live_draft_bids_set.yaml +0 -0
  129. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/cassettes/test_integration_live_write/test_live_draft_campaign_create_get_delete.yaml +0 -0
  130. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/cassettes/test_integration_live_write/test_live_draft_creatives_chain_advideo_to_creative.yaml +0 -0
  131. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/cassettes/test_integration_live_write/test_live_draft_dynamicads_add_delete.yaml +0 -0
  132. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/cassettes/test_integration_live_write/test_live_draft_dynamicads_suspend_resume.yaml +0 -0
  133. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/cassettes/test_integration_live_write/test_live_draft_keywordbids_set.yaml +0 -0
  134. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/cassettes/test_integration_live_write/test_live_draft_keywords_add_update_delete.yaml +0 -0
  135. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/cassettes/test_integration_live_write/test_live_draft_keywords_suspend_resume.yaml +0 -0
  136. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/cassettes/test_integration_live_write/test_live_draft_sitelinks_add_get_delete.yaml +0 -0
  137. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/cassettes/test_integration_live_write/test_live_draft_smartadtargets_add_update_delete.yaml +0 -0
  138. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/cassettes/test_integration_live_write/test_live_draft_smartadtargets_suspend_resume.yaml +0 -0
  139. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/cassettes/test_integration_write/TestWriteAdExtensions.test_add_delete.yaml +0 -0
  140. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/cassettes/test_integration_write/TestWriteAdGroups.test_add_update_delete.yaml +0 -0
  141. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/cassettes/test_integration_write/TestWriteAdImages.test_add_delete.yaml +0 -0
  142. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/cassettes/test_integration_write/TestWriteAds.test_add_text_ad_update_delete.yaml +0 -0
  143. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/cassettes/test_integration_write/TestWriteAudienceTargets.test_add_delete.yaml +0 -0
  144. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/cassettes/test_integration_write/TestWriteBidModifiersAdd.test_add_delete_mobile.yaml +0 -0
  145. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/cassettes/test_integration_write/TestWriteBidModifiersSet.test_set_without_id_is_rejected.yaml +0 -0
  146. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/cassettes/test_integration_write/TestWriteBids.test_set_bid.yaml +0 -0
  147. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/cassettes/test_integration_write/TestWriteCampaignDraftLifecycle.test_draft_create_get_delete.yaml +0 -0
  148. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/cassettes/test_integration_write/TestWriteCampaigns.test_campaign_lifecycle.yaml +0 -0
  149. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/cassettes/test_integration_write/TestWriteDynamicAds.test_add_update_delete.yaml +0 -0
  150. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/cassettes/test_integration_write/TestWriteFeeds.test_add_update_delete.yaml +0 -0
  151. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/cassettes/test_integration_write/TestWriteKeywordBids.test_set_keyword_bid.yaml +0 -0
  152. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/cassettes/test_integration_write/TestWriteKeywords.test_add_update_delete.yaml +0 -0
  153. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/cassettes/test_integration_write/TestWriteNegativeKeywordSharedSets.test_add_update_delete.yaml +0 -0
  154. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/cassettes/test_integration_write/TestWriteRetargeting.test_add_delete.yaml +0 -0
  155. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/cassettes/test_integration_write/TestWriteSitelinks.test_add_delete.yaml +0 -0
  156. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/cassettes/test_integration_write/TestWriteSmartAdTargets.test_add_update_delete.yaml +0 -0
  157. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/cassettes/test_integration_write/TestWriteVCards.test_add_delete.yaml +0 -0
  158. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/conftest.py +0 -0
  159. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/fixtures/test-video.mp4 +0 -0
  160. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/reports_cache/raw/fields-list.html +0 -0
  161. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/reports_cache/raw/headers.html +0 -0
  162. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/reports_cache/raw/type.html +0 -0
  163. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/test_auth_bw.py +0 -0
  164. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/test_auth_oauth.py +0 -0
  165. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/test_auth_op.py +0 -0
  166. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/test_balance.py +0 -0
  167. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/test_cli.py +0 -0
  168. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/test_comprehensive.py +0 -0
  169. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/test_integration.py +0 -0
  170. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/test_integration_live_write.py +0 -0
  171. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/test_integration_write.py +0 -0
  172. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/test_reports_drift.py +0 -0
  173. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/test_transport_contract.py +0 -0
  174. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/test_v4goals.py +0 -0
  175. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/test_vendor_imports.py +0 -0
  176. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/wsdl_cache/adextensions.xml +0 -0
  177. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/wsdl_cache/adgroups.xml +0 -0
  178. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/wsdl_cache/adimages.xml +0 -0
  179. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/wsdl_cache/ads.xml +0 -0
  180. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/wsdl_cache/advideos.xml +0 -0
  181. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/wsdl_cache/agencyclients.xml +0 -0
  182. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/wsdl_cache/audiencetargets.xml +0 -0
  183. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/wsdl_cache/bidmodifiers.xml +0 -0
  184. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/wsdl_cache/bids.xml +0 -0
  185. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/wsdl_cache/businesses.xml +0 -0
  186. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/wsdl_cache/campaigns.xml +0 -0
  187. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/wsdl_cache/changes.xml +0 -0
  188. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/wsdl_cache/clients.xml +0 -0
  189. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/wsdl_cache/creatives.xml +0 -0
  190. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/wsdl_cache/dictionaries.xml +0 -0
  191. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/wsdl_cache/dynamicfeedadtargets.xml +0 -0
  192. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/wsdl_cache/dynamictextadtargets.xml +0 -0
  193. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/wsdl_cache/feeds.xml +0 -0
  194. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/wsdl_cache/imports/adextensiontypes.xsd +0 -0
  195. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/wsdl_cache/imports/general.xsd +0 -0
  196. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/wsdl_cache/imports/generalclients.xsd +0 -0
  197. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/wsdl_cache/keywordbids.xml +0 -0
  198. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/wsdl_cache/keywords.xml +0 -0
  199. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/wsdl_cache/keywordsresearch.xml +0 -0
  200. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/wsdl_cache/leads.xml +0 -0
  201. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/wsdl_cache/negativekeywordsharedsets.xml +0 -0
  202. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/wsdl_cache/retargetinglists.xml +0 -0
  203. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/wsdl_cache/sitelinks.xml +0 -0
  204. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/wsdl_cache/smartadtargets.xml +0 -0
  205. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/wsdl_cache/strategies.xml +0 -0
  206. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/wsdl_cache/turbopages.xml +0 -0
  207. {direct_cli-0.3.1 → direct_cli-0.3.2}/tests/wsdl_cache/vcards.xml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: direct-cli
3
- Version: 0.3.1
3
+ Version: 0.3.2
4
4
  Summary: Command-line interface for Yandex Direct API
5
5
  Author: axisrow
6
6
  License: MIT
@@ -147,6 +147,39 @@ direct v4goals get-retargeting-goals --campaign-ids 123,456 --format table
147
147
  direct v4goals get-stat-goals --campaign-ids 123 --dry-run
148
148
  ```
149
149
 
150
+ ### V4 Live Events
151
+
152
+ ```bash
153
+ direct v4events get-events-log --from 2026-04-14T00:00:00 --to 2026-04-15T00:00:00
154
+ 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
155
+ ```
156
+
157
+ ### V4 Live Finance
158
+
159
+ `get-credit-limits` requires a financial token and operation number. Pass them
160
+ with `--finance-token` and `--operation-num`, or set
161
+ `YANDEX_DIRECT_FINANCE_TOKEN` and `YANDEX_DIRECT_OPERATION_NUM`.
162
+ Money mutation commands are dry-run-only in this release and always require
163
+ `--dry-run`; dry-run output masks the financial token.
164
+
165
+ ```bash
166
+ direct v4finance get-credit-limits --logins client-login --finance-token FINANCE_TOKEN --operation-num 123
167
+ direct v4finance get-credit-limits --logins client-login,other-client --format table
168
+ direct v4finance check-payment --custom-transaction-id A123456789012345678901234567890B
169
+ direct v4finance transfer-money --from-campaign-id 123 --to-campaign-id 456 --amount 100.50 --finance-token FINANCE_TOKEN --operation-num 123 --dry-run
170
+ direct v4finance pay-campaigns --campaign-id 123 --amount 100.50 --contract-id CONTRACT_ID --pay-method CREDIT --finance-token FINANCE_TOKEN --operation-num 123 --dry-run
171
+ ```
172
+
173
+ ### V4 Live Shared Account
174
+
175
+ Shared-account mutations are dry-run-only in this release and always require
176
+ `--dry-run`.
177
+
178
+ ```bash
179
+ direct v4account enable-shared-account --client-login client-login --dry-run
180
+ direct v4account account-management --action Update --account-id 1327944 --day-budget 100.50 --spend-mode Default --money-in-sms Yes --money-out-sms No --email ops@example.com --money-warning-value 25 --dry-run
181
+ ```
182
+
150
183
  ### CLI Convention
151
184
 
152
185
  The current CLI convention is defined as follows.
@@ -344,6 +377,7 @@ direct keywords delete --id 88888
344
377
  ```bash
345
378
  # Get a report (saved to file)
346
379
  direct reports get --type CAMPAIGN_PERFORMANCE_REPORT --from 2024-01-01 --to 2024-01-31 --name "January Report" --fields "Date,CampaignId,Clicks,Cost" --format csv --output report.csv
380
+ direct reports get --type CUSTOM_REPORT --from 2024-01-01 --to 2024-01-31 --name "Goals Report" --fields "Date,CampaignId,GoalsRoi" --goals 12345,67890 --attribution-models AUTO --format csv --output goals-report.csv
347
381
 
348
382
  # List available report types
349
383
  direct reports list-types
@@ -509,9 +543,9 @@ Current command surface:
509
543
  | WSDL-backed API services | 29 |
510
544
  | Supported API services including Reports | 30 |
511
545
  | WSDL operations | 112 |
512
- | CLI groups including `auth` | 31 |
513
- | CLI subcommands including `auth` | 122 |
514
- | API CLI subcommands excluding `auth` | 118 |
546
+ | CLI groups including `auth` | 39 |
547
+ | CLI subcommands including `auth` | 130 |
548
+ | API CLI subcommands excluding `auth` | 126 |
515
549
 
516
550
  ### API Coverage And Drift Monitoring
517
551
 
@@ -966,6 +1000,7 @@ direct keywords delete --id 88888
966
1000
  ```bash
967
1001
  # Сформировать отчёт (сохраняется в файл)
968
1002
  direct reports get --type CAMPAIGN_PERFORMANCE_REPORT --from 2024-01-01 --to 2024-01-31 --name "Отчёт за январь" --fields "Date,CampaignId,Clicks,Cost" --format csv --output report.csv
1003
+ direct reports get --type CUSTOM_REPORT --from 2024-01-01 --to 2024-01-31 --name "Отчёт по целям" --fields "Date,CampaignId,GoalsRoi" --goals 12345,67890 --attribution-models AUTO --format csv --output goals-report.csv
969
1004
 
970
1005
  # Список доступных типов отчётов
971
1006
  direct reports list-types
@@ -1132,9 +1167,9 @@ YANDEX_DIRECT_LIVE_WRITE=1 pytest -m integration_live_write -v --record-mode=rew
1132
1167
  | WSDL-backed API services | 29 |
1133
1168
  | API services с учётом Reports | 30 |
1134
1169
  | WSDL operations | 112 |
1135
- | CLI groups с `auth` | 31 |
1136
- | CLI subcommands с `auth` | 122 |
1137
- | API CLI subcommands без `auth` | 118 |
1170
+ | CLI groups с `auth` | 39 |
1171
+ | CLI subcommands с `auth` | 130 |
1172
+ | API CLI subcommands без `auth` | 126 |
1138
1173
 
1139
1174
  #### Live sandbox write smoke
1140
1175
 
@@ -108,6 +108,39 @@ direct v4goals get-retargeting-goals --campaign-ids 123,456 --format table
108
108
  direct v4goals get-stat-goals --campaign-ids 123 --dry-run
109
109
  ```
110
110
 
111
+ ### V4 Live Events
112
+
113
+ ```bash
114
+ direct v4events get-events-log --from 2026-04-14T00:00:00 --to 2026-04-15T00:00:00
115
+ 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
116
+ ```
117
+
118
+ ### V4 Live Finance
119
+
120
+ `get-credit-limits` requires a financial token and operation number. Pass them
121
+ with `--finance-token` and `--operation-num`, or set
122
+ `YANDEX_DIRECT_FINANCE_TOKEN` and `YANDEX_DIRECT_OPERATION_NUM`.
123
+ Money mutation commands are dry-run-only in this release and always require
124
+ `--dry-run`; dry-run output masks the financial token.
125
+
126
+ ```bash
127
+ direct v4finance get-credit-limits --logins client-login --finance-token FINANCE_TOKEN --operation-num 123
128
+ direct v4finance get-credit-limits --logins client-login,other-client --format table
129
+ direct v4finance check-payment --custom-transaction-id A123456789012345678901234567890B
130
+ direct v4finance transfer-money --from-campaign-id 123 --to-campaign-id 456 --amount 100.50 --finance-token FINANCE_TOKEN --operation-num 123 --dry-run
131
+ direct v4finance pay-campaigns --campaign-id 123 --amount 100.50 --contract-id CONTRACT_ID --pay-method CREDIT --finance-token FINANCE_TOKEN --operation-num 123 --dry-run
132
+ ```
133
+
134
+ ### V4 Live Shared Account
135
+
136
+ Shared-account mutations are dry-run-only in this release and always require
137
+ `--dry-run`.
138
+
139
+ ```bash
140
+ direct v4account enable-shared-account --client-login client-login --dry-run
141
+ direct v4account account-management --action Update --account-id 1327944 --day-budget 100.50 --spend-mode Default --money-in-sms Yes --money-out-sms No --email ops@example.com --money-warning-value 25 --dry-run
142
+ ```
143
+
111
144
  ### CLI Convention
112
145
 
113
146
  The current CLI convention is defined as follows.
@@ -305,6 +338,7 @@ direct keywords delete --id 88888
305
338
  ```bash
306
339
  # Get a report (saved to file)
307
340
  direct reports get --type CAMPAIGN_PERFORMANCE_REPORT --from 2024-01-01 --to 2024-01-31 --name "January Report" --fields "Date,CampaignId,Clicks,Cost" --format csv --output report.csv
341
+ direct reports get --type CUSTOM_REPORT --from 2024-01-01 --to 2024-01-31 --name "Goals Report" --fields "Date,CampaignId,GoalsRoi" --goals 12345,67890 --attribution-models AUTO --format csv --output goals-report.csv
308
342
 
309
343
  # List available report types
310
344
  direct reports list-types
@@ -470,9 +504,9 @@ Current command surface:
470
504
  | WSDL-backed API services | 29 |
471
505
  | Supported API services including Reports | 30 |
472
506
  | WSDL operations | 112 |
473
- | CLI groups including `auth` | 31 |
474
- | CLI subcommands including `auth` | 122 |
475
- | API CLI subcommands excluding `auth` | 118 |
507
+ | CLI groups including `auth` | 39 |
508
+ | CLI subcommands including `auth` | 130 |
509
+ | API CLI subcommands excluding `auth` | 126 |
476
510
 
477
511
  ### API Coverage And Drift Monitoring
478
512
 
@@ -927,6 +961,7 @@ direct keywords delete --id 88888
927
961
  ```bash
928
962
  # Сформировать отчёт (сохраняется в файл)
929
963
  direct reports get --type CAMPAIGN_PERFORMANCE_REPORT --from 2024-01-01 --to 2024-01-31 --name "Отчёт за январь" --fields "Date,CampaignId,Clicks,Cost" --format csv --output report.csv
964
+ direct reports get --type CUSTOM_REPORT --from 2024-01-01 --to 2024-01-31 --name "Отчёт по целям" --fields "Date,CampaignId,GoalsRoi" --goals 12345,67890 --attribution-models AUTO --format csv --output goals-report.csv
930
965
 
931
966
  # Список доступных типов отчётов
932
967
  direct reports list-types
@@ -1093,9 +1128,9 @@ YANDEX_DIRECT_LIVE_WRITE=1 pytest -m integration_live_write -v --record-mode=rew
1093
1128
  | WSDL-backed API services | 29 |
1094
1129
  | API services с учётом Reports | 30 |
1095
1130
  | WSDL operations | 112 |
1096
- | CLI groups с `auth` | 31 |
1097
- | CLI subcommands с `auth` | 122 |
1098
- | API CLI subcommands без `auth` | 118 |
1131
+ | CLI groups с `auth` | 39 |
1132
+ | CLI subcommands с `auth` | 130 |
1133
+ | API CLI subcommands без `auth` | 126 |
1099
1134
 
1100
1135
  #### Live sandbox write smoke
1101
1136
 
@@ -43,11 +43,14 @@ class V4LiveClientAdapter(JSONAdapterMixin, TapiAdapter):
43
43
  token = api_params.get("access_token")
44
44
  login = api_params.get("login")
45
45
  language = api_params.get("language", "en")
46
-
47
- # Enrich the JSON body with token / locale. format_data_to_request does
48
- # not see api_params, so we do this here, after super() has already
49
- # serialised the user data. Agency/client selection is transport-level
50
- # (Client-Login header); method params must stay schema-shaped.
46
+ finance_token = api_params.get("finance_token")
47
+ operation_num = api_params.get("operation_num")
48
+
49
+ # Enrich the JSON body with top-level v4 Live fields.
50
+ # format_data_to_request does not see api_params, so we do this here,
51
+ # after super() has already serialised the user data. Agency/client
52
+ # selection is transport-level (Client-Login header); method params
53
+ # must stay schema-shaped.
51
54
  raw = params.get("data")
52
55
  if raw:
53
56
  if isinstance(raw, (bytes, bytearray)):
@@ -67,6 +70,10 @@ class V4LiveClientAdapter(JSONAdapterMixin, TapiAdapter):
67
70
  body.setdefault("token", token)
68
71
  if language:
69
72
  body.setdefault("locale", language)
73
+ if finance_token:
74
+ body.setdefault("finance_token", finance_token)
75
+ if operation_num is not None:
76
+ body.setdefault("operation_num", operation_num)
70
77
 
71
78
  params["data"] = orjson.dumps(body)
72
79
 
@@ -49,6 +49,8 @@ class YandexDirectV4Live:
49
49
  language: str = "en",
50
50
  retry_if_exceeded_limit: bool = True,
51
51
  retries_if_server_error: int = 5,
52
+ finance_token: Optional[str] = None,
53
+ operation_num: Optional[int] = None,
52
54
  ) -> None: ...
53
55
 
54
56
  def v4live(self) -> V4LiveExecutor: ...
@@ -80,6 +80,8 @@ def create_v4_client(
80
80
  language: Optional[str] = None,
81
81
  retry_if_exceeded_limit: bool = True,
82
82
  retries_if_server_error: int = 5,
83
+ finance_token: Optional[str] = None,
84
+ operation_num: Optional[int] = None,
83
85
  ) -> YandexDirectV4Live:
84
86
  """
85
87
  Create YandexDirect v4 Live client.
@@ -96,6 +98,8 @@ def create_v4_client(
96
98
  language: API locale
97
99
  retry_if_exceeded_limit: Retry when the API limit is exceeded
98
100
  retries_if_server_error: Number of retries for server errors
101
+ finance_token: Financial token for v4 Live finance methods
102
+ operation_num: Financial operation number for v4 Live finance methods
99
103
 
100
104
  Returns:
101
105
  YandexDirect v4 Live client instance
@@ -117,6 +121,8 @@ def create_v4_client(
117
121
  language=language or "en",
118
122
  retry_if_exceeded_limit=retry_if_exceeded_limit,
119
123
  retries_if_server_error=retries_if_server_error,
124
+ finance_token=finance_token,
125
+ operation_num=operation_num,
120
126
  )
121
127
 
122
128
 
@@ -42,10 +42,10 @@ from .commands.dynamicfeedadtargets import dynamicfeedadtargets
42
42
  from .commands.strategies import strategies
43
43
  from .commands.auth import auth
44
44
  from .commands.balance import balance
45
+ from .commands.v4events import v4events
46
+ from .commands.v4finance import v4finance
47
+ from .commands.v4account import v4account
45
48
  from .commands.v4shells import (
46
- v4account,
47
- v4events,
48
- v4finance,
49
49
  v4forecast,
50
50
  v4meta,
51
51
  v4wordstat,
@@ -6,7 +6,7 @@ import click
6
6
 
7
7
  from ..api import create_client
8
8
  from ..output import format_output, print_error
9
- from ..utils import get_default_fields, parse_ids
9
+ from ..utils import add_criteria_csv, get_default_fields, parse_ids
10
10
 
11
11
 
12
12
  @click.group()
@@ -17,13 +17,30 @@ def adextensions():
17
17
  @adextensions.command()
18
18
  @click.option("--ids", help="Comma-separated extension IDs")
19
19
  @click.option("--types", help="Filter by types")
20
+ @click.option("--states", help="Comma-separated states")
21
+ @click.option("--statuses", help="Comma-separated statuses")
22
+ @click.option("--modified-since", help="ModifiedSince datetime")
20
23
  @click.option("--limit", type=int, help="Limit number of results")
21
24
  @click.option("--fetch-all", is_flag=True, help="Fetch all pages")
22
25
  @click.option("--format", "output_format", default="json", help="Output format")
23
26
  @click.option("--output", help="Output file")
24
27
  @click.option("--fields", help="Comma-separated field names")
28
+ @click.option("--dry-run", is_flag=True, help="Show request without sending")
25
29
  @click.pass_context
26
- def get(ctx, ids, types, limit, fetch_all, output_format, output, fields):
30
+ def get(
31
+ ctx,
32
+ ids,
33
+ types,
34
+ states,
35
+ statuses,
36
+ modified_since,
37
+ limit,
38
+ fetch_all,
39
+ output_format,
40
+ output,
41
+ fields,
42
+ dry_run,
43
+ ):
27
44
  """Get ad extensions"""
28
45
  try:
29
46
  client = create_client(
@@ -41,6 +58,10 @@ def get(ctx, ids, types, limit, fetch_all, output_format, output, fields):
41
58
  criteria["Ids"] = parse_ids(ids)
42
59
  if types:
43
60
  criteria["Types"] = types.split(",")
61
+ add_criteria_csv(criteria, "States", states, upper=True)
62
+ add_criteria_csv(criteria, "Statuses", statuses, upper=True)
63
+ if modified_since:
64
+ criteria["ModifiedSince"] = modified_since
44
65
 
45
66
  params = {"SelectionCriteria": criteria, "FieldNames": field_names}
46
67
 
@@ -49,6 +70,10 @@ def get(ctx, ids, types, limit, fetch_all, output_format, output, fields):
49
70
 
50
71
  body = {"method": "get", "params": params}
51
72
 
73
+ if dry_run:
74
+ format_output(body, "json", None)
75
+ return
76
+
52
77
  result = client.adextensions().post(data=body)
53
78
 
54
79
  if fetch_all:
@@ -6,7 +6,7 @@ import click
6
6
 
7
7
  from ..api import create_client
8
8
  from ..output import format_output, print_error
9
- from ..utils import get_default_fields, parse_ids
9
+ from ..utils import add_criteria_csv, get_default_fields, parse_ids
10
10
 
11
11
 
12
12
  @click.group()
@@ -18,26 +18,46 @@ def adgroups():
18
18
  @click.option("--ids", help="Comma-separated ad group IDs")
19
19
  @click.option("--campaign-ids", help="Comma-separated campaign IDs")
20
20
  @click.option("--status", help="Filter by status")
21
+ @click.option("--statuses", help="Comma-separated statuses")
21
22
  @click.option("--types", help="Filter by types")
23
+ @click.option("--tag-ids", help="Comma-separated tag IDs")
24
+ @click.option("--tags", help="Comma-separated tag names")
25
+ @click.option("--app-icon-statuses", help="Comma-separated app icon statuses")
26
+ @click.option("--serving-statuses", help="Comma-separated serving statuses")
27
+ @click.option(
28
+ "--negative-keyword-shared-set-ids",
29
+ help="Comma-separated negative keyword shared set IDs",
30
+ )
22
31
  @click.option("--limit", type=int, help="Limit number of results")
23
32
  @click.option("--fetch-all", is_flag=True, help="Fetch all pages")
24
33
  @click.option("--format", "output_format", default="json", help="Output format")
25
34
  @click.option("--output", help="Output file")
26
35
  @click.option("--fields", help="Comma-separated field names")
36
+ @click.option("--dry-run", is_flag=True, help="Show request without sending")
27
37
  @click.pass_context
28
38
  def get(
29
39
  ctx,
30
40
  ids,
31
41
  campaign_ids,
32
42
  status,
43
+ statuses,
33
44
  types,
45
+ tag_ids,
46
+ tags,
47
+ app_icon_statuses,
48
+ serving_statuses,
49
+ negative_keyword_shared_set_ids,
34
50
  limit,
35
51
  fetch_all,
36
52
  output_format,
37
53
  output,
38
54
  fields,
55
+ dry_run,
39
56
  ):
40
57
  """Get ad groups"""
58
+ if status and statuses:
59
+ raise click.UsageError("--status and --statuses are mutually exclusive")
60
+
41
61
  try:
42
62
  client = create_client(
43
63
  token=ctx.obj.get("token"),
@@ -54,8 +74,19 @@ def get(
54
74
  criteria["CampaignIds"] = parse_ids(campaign_ids)
55
75
  if status:
56
76
  criteria["Statuses"] = [status]
77
+ add_criteria_csv(criteria, "Statuses", statuses, upper=True)
57
78
  if types:
58
79
  criteria["Types"] = types.split(",")
80
+ add_criteria_csv(criteria, "TagIds", tag_ids, integers=True)
81
+ add_criteria_csv(criteria, "Tags", tags)
82
+ add_criteria_csv(criteria, "AppIconStatuses", app_icon_statuses, upper=True)
83
+ add_criteria_csv(criteria, "ServingStatuses", serving_statuses, upper=True)
84
+ add_criteria_csv(
85
+ criteria,
86
+ "NegativeKeywordSharedSetIds",
87
+ negative_keyword_shared_set_ids,
88
+ integers=True,
89
+ )
59
90
 
60
91
  params = {"SelectionCriteria": criteria, "FieldNames": field_names}
61
92
 
@@ -64,6 +95,10 @@ def get(
64
95
 
65
96
  body = {"method": "get", "params": params}
66
97
 
98
+ if dry_run:
99
+ format_output(body, "json", None)
100
+ return
101
+
67
102
  result = client.adgroups().post(data=body)
68
103
 
69
104
  if fetch_all:
@@ -6,7 +6,7 @@ import click
6
6
 
7
7
  from ..api import create_client
8
8
  from ..output import format_output, print_error
9
- from ..utils import get_default_fields, load_base64_file, parse_ids
9
+ from ..utils import add_criteria_csv, get_default_fields, load_base64_file, parse_ids
10
10
 
11
11
 
12
12
  @click.group()
@@ -16,13 +16,27 @@ def adimages():
16
16
 
17
17
  @adimages.command()
18
18
  @click.option("--ids", help="Comma-separated image IDs")
19
+ @click.option("--image-hashes", help="Comma-separated ad image hashes")
20
+ @click.option("--associated", type=click.Choice(["YES", "NO"], case_sensitive=False))
19
21
  @click.option("--limit", type=int, help="Limit number of results")
20
22
  @click.option("--fetch-all", is_flag=True, help="Fetch all pages")
21
23
  @click.option("--format", "output_format", default="json", help="Output format")
22
24
  @click.option("--output", help="Output file")
23
25
  @click.option("--fields", help="Comma-separated field names")
26
+ @click.option("--dry-run", is_flag=True, help="Show request without sending")
24
27
  @click.pass_context
25
- def get(ctx, ids, limit, fetch_all, output_format, output, fields):
28
+ def get(
29
+ ctx,
30
+ ids,
31
+ image_hashes,
32
+ associated,
33
+ limit,
34
+ fetch_all,
35
+ output_format,
36
+ output,
37
+ fields,
38
+ dry_run,
39
+ ):
26
40
  """Get ad images"""
27
41
  try:
28
42
  client = create_client(
@@ -31,21 +45,28 @@ def get(ctx, ids, limit, fetch_all, output_format, output, fields):
31
45
  sandbox=ctx.obj.get("sandbox"),
32
46
  )
33
47
 
34
- field_names = (
35
- fields.split(",") if fields else get_default_fields("adimages")
36
- )
48
+ field_names = fields.split(",") if fields else get_default_fields("adimages")
37
49
 
38
50
  criteria = {}
39
51
  if ids:
40
52
  criteria["Ids"] = parse_ids(ids)
53
+ add_criteria_csv(criteria, "AdImageHashes", image_hashes)
54
+ if associated:
55
+ criteria["Associated"] = associated.upper()
41
56
 
42
- params = {"SelectionCriteria": criteria, "FieldNames": field_names}
57
+ params = {"FieldNames": field_names}
58
+ if criteria:
59
+ params["SelectionCriteria"] = criteria
43
60
 
44
61
  if limit:
45
62
  params["Page"] = {"Limit": limit}
46
63
 
47
64
  body = {"method": "get", "params": params}
48
65
 
66
+ if dry_run:
67
+ format_output(body, "json", None)
68
+ return
69
+
49
70
  result = client.adimages().post(data=body)
50
71
 
51
72
  if fetch_all:
@@ -6,7 +6,7 @@ import click
6
6
 
7
7
  from ..api import create_client
8
8
  from ..output import format_output, print_error
9
- from ..utils import get_default_fields, parse_ids
9
+ from ..utils import add_criteria_csv, get_default_fields, parse_ids
10
10
 
11
11
 
12
12
  @click.group()
@@ -19,6 +19,24 @@ def ads():
19
19
  @click.option("--campaign-ids", help="Comma-separated campaign IDs")
20
20
  @click.option("--adgroup-ids", help="Comma-separated ad group IDs")
21
21
  @click.option("--status", help="Filter by status")
22
+ @click.option("--statuses", help="Comma-separated statuses")
23
+ @click.option("--states", help="Comma-separated states")
24
+ @click.option("--types", help="Comma-separated ad types")
25
+ @click.option("--mobile", type=click.Choice(["YES", "NO"], case_sensitive=False))
26
+ @click.option("--vcard-ids", help="Comma-separated vCard IDs")
27
+ @click.option("--sitelink-set-ids", help="Comma-separated sitelink set IDs")
28
+ @click.option("--image-hashes", help="Comma-separated ad image hashes")
29
+ @click.option(
30
+ "--vcard-moderation-statuses", help="Comma-separated vCard moderation statuses"
31
+ )
32
+ @click.option(
33
+ "--sitelinks-moderation-statuses",
34
+ help="Comma-separated sitelinks moderation statuses",
35
+ )
36
+ @click.option(
37
+ "--image-moderation-statuses", help="Comma-separated image moderation statuses"
38
+ )
39
+ @click.option("--adextension-ids", help="Comma-separated ad extension IDs")
22
40
  @click.option("--limit", type=int, help="Limit number of results")
23
41
  @click.option("--fetch-all", is_flag=True, help="Fetch all pages")
24
42
  @click.option("--format", "output_format", default="json", help="Output format")
@@ -35,6 +53,17 @@ def get(
35
53
  campaign_ids,
36
54
  adgroup_ids,
37
55
  status,
56
+ statuses,
57
+ states,
58
+ types,
59
+ mobile,
60
+ vcard_ids,
61
+ sitelink_set_ids,
62
+ image_hashes,
63
+ vcard_moderation_statuses,
64
+ sitelinks_moderation_statuses,
65
+ image_moderation_statuses,
66
+ adextension_ids,
38
67
  limit,
39
68
  fetch_all,
40
69
  output_format,
@@ -44,6 +73,9 @@ def get(
44
73
  dry_run,
45
74
  ):
46
75
  """Get ads"""
76
+ if status and statuses:
77
+ raise click.UsageError("--status and --statuses are mutually exclusive")
78
+
47
79
  try:
48
80
  field_names = (
49
81
  fields.split(",") if fields else get_default_fields("ads", "FieldNames")
@@ -64,6 +96,27 @@ def get(
64
96
  criteria["AdGroupIds"] = parse_ids(adgroup_ids)
65
97
  if status:
66
98
  criteria["Statuses"] = [status]
99
+ add_criteria_csv(criteria, "Statuses", statuses, upper=True)
100
+ add_criteria_csv(criteria, "States", states, upper=True)
101
+ add_criteria_csv(criteria, "Types", types, upper=True)
102
+ if mobile:
103
+ criteria["Mobile"] = mobile.upper()
104
+ add_criteria_csv(criteria, "VCardIds", vcard_ids, integers=True)
105
+ add_criteria_csv(criteria, "SitelinkSetIds", sitelink_set_ids, integers=True)
106
+ add_criteria_csv(criteria, "AdImageHashes", image_hashes)
107
+ add_criteria_csv(
108
+ criteria, "VCardModerationStatuses", vcard_moderation_statuses, upper=True
109
+ )
110
+ add_criteria_csv(
111
+ criteria,
112
+ "SitelinksModerationStatuses",
113
+ sitelinks_moderation_statuses,
114
+ upper=True,
115
+ )
116
+ add_criteria_csv(
117
+ criteria, "AdImageModerationStatuses", image_moderation_statuses, upper=True
118
+ )
119
+ add_criteria_csv(criteria, "AdExtensionIds", adextension_ids, integers=True)
67
120
 
68
121
  params = {
69
122
  "SelectionCriteria": criteria,
@@ -139,9 +192,7 @@ def add(ctx, adgroup_id, ad_type, title, text, href, image_hash, dry_run):
139
192
  if not value
140
193
  ]
141
194
  if missing_fields:
142
- raise click.UsageError(
143
- "TEXT_AD requires " + ", ".join(missing_fields)
144
- )
195
+ raise click.UsageError("TEXT_AD requires " + ", ".join(missing_fields))
145
196
  ad_data["TextAd"] = {
146
197
  "Mobile": "NO",
147
198
  "Title": title,
@@ -15,14 +15,15 @@ def advideos():
15
15
 
16
16
 
17
17
  @advideos.command()
18
- @click.option("--ids", help="Comma-separated video IDs")
18
+ @click.option("--ids", required=True, help="Comma-separated video IDs")
19
19
  @click.option("--limit", type=int, help="Limit number of results")
20
20
  @click.option("--fetch-all", is_flag=True, help="Fetch all pages")
21
21
  @click.option("--format", "output_format", default="json", help="Output format")
22
22
  @click.option("--output", help="Output file")
23
23
  @click.option("--fields", help="Comma-separated field names")
24
+ @click.option("--dry-run", is_flag=True, help="Show request without sending")
24
25
  @click.pass_context
25
- def get(ctx, ids, limit, fetch_all, output_format, output, fields):
26
+ def get(ctx, ids, limit, fetch_all, output_format, output, fields, dry_run):
26
27
  """Get ad videos"""
27
28
  try:
28
29
  client = create_client(
@@ -33,9 +34,7 @@ def get(ctx, ids, limit, fetch_all, output_format, output, fields):
33
34
 
34
35
  field_names = fields.split(",") if fields else get_default_fields("advideos")
35
36
 
36
- criteria = {}
37
- if ids:
38
- criteria["Ids"] = [x.strip() for x in ids.split(",")]
37
+ criteria = {"Ids": [x.strip() for x in ids.split(",") if x.strip()]}
39
38
 
40
39
  params = {
41
40
  "SelectionCriteria": criteria,
@@ -47,6 +46,10 @@ def get(ctx, ids, limit, fetch_all, output_format, output, fields):
47
46
 
48
47
  body = {"method": "get", "params": params}
49
48
 
49
+ if dry_run:
50
+ format_output(body, "json", None)
51
+ return
52
+
50
53
  result = client.advideos().post(data=body)
51
54
 
52
55
  if fetch_all:
@@ -64,7 +67,9 @@ def get(ctx, ids, limit, fetch_all, output_format, output, fields):
64
67
 
65
68
 
66
69
  @advideos.command()
67
- @click.option("--url", help="Video URL (mutually exclusive with --video-data/--video-file)")
70
+ @click.option(
71
+ "--url", help="Video URL (mutually exclusive with --video-data/--video-file)"
72
+ )
68
73
  @click.option("--video-data", help="Base64-encoded video binary")
69
74
  @click.option("--video-file", help="Path to a video file to base64-encode")
70
75
  @click.option("--name", help="Video name")