@daemux/store-automator 0.10.27 → 0.10.29

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.
@@ -5,14 +5,14 @@
5
5
  },
6
6
  "metadata": {
7
7
  "description": "App Store & Google Play automation for Flutter apps",
8
- "version": "0.10.27"
8
+ "version": "0.10.29"
9
9
  },
10
10
  "plugins": [
11
11
  {
12
12
  "name": "store-automator",
13
13
  "source": "./plugins/store-automator",
14
14
  "description": "3 agents for app store publishing: reviewer, meta-creator, media-designer",
15
- "version": "0.10.27",
15
+ "version": "0.10.29",
16
16
  "keywords": [
17
17
  "flutter",
18
18
  "app-store",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@daemux/store-automator",
3
- "version": "0.10.27",
3
+ "version": "0.10.29",
4
4
  "description": "Full App Store & Google Play automation for Flutter apps with Claude Code agents",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "store-automator",
3
- "version": "0.10.27",
3
+ "version": "0.10.29",
4
4
  "description": "App Store & Google Play automation agents for Flutter app publishing",
5
5
  "author": {
6
6
  "name": "Daemux"
@@ -204,6 +204,72 @@ def update_localization(headers: dict, loc_id: str, loc_data: dict) -> None:
204
204
  print(f" Updated localization (ID: {loc_id})")
205
205
 
206
206
 
207
+ def get_group_localizations(headers: dict, group_id: str) -> list:
208
+ """Fetch existing localizations for a subscription group."""
209
+ resp = requests.get(
210
+ f"{BASE_URL}/subscriptionGroups/{group_id}/subscriptionGroupLocalizations",
211
+ headers=headers,
212
+ timeout=TIMEOUT,
213
+ )
214
+ resp.raise_for_status()
215
+ return resp.json().get("data", [])
216
+
217
+
218
+ def create_group_localization(
219
+ headers: dict, group_id: str, locale: str, name: str, custom_app_name: str = None,
220
+ ) -> None:
221
+ """Create a new localization for a subscription group."""
222
+ resp = requests.post(
223
+ f"{BASE_URL}/subscriptionGroupLocalizations",
224
+ json={
225
+ "data": {
226
+ "type": "subscriptionGroupLocalizations",
227
+ "attributes": {
228
+ "name": name,
229
+ "customAppName": custom_app_name,
230
+ "locale": locale,
231
+ },
232
+ "relationships": {
233
+ "subscriptionGroup": {
234
+ "data": {"type": "subscriptionGroups", "id": group_id}
235
+ }
236
+ },
237
+ }
238
+ },
239
+ headers=headers,
240
+ timeout=TIMEOUT,
241
+ )
242
+ if not resp.ok:
243
+ print_api_errors(resp, f"create group localization '{locale}' for group {group_id}")
244
+ return
245
+ print(f" Created group localization '{locale}'")
246
+
247
+
248
+ def update_group_localization(
249
+ headers: dict, loc_id: str, name: str, custom_app_name: str = None,
250
+ ) -> None:
251
+ """Update an existing subscription group localization."""
252
+ resp = requests.patch(
253
+ f"{BASE_URL}/subscriptionGroupLocalizations/{loc_id}",
254
+ json={
255
+ "data": {
256
+ "type": "subscriptionGroupLocalizations",
257
+ "id": loc_id,
258
+ "attributes": {
259
+ "name": name,
260
+ "customAppName": custom_app_name,
261
+ },
262
+ }
263
+ },
264
+ headers=headers,
265
+ timeout=TIMEOUT,
266
+ )
267
+ if not resp.ok:
268
+ print_api_errors(resp, f"update group localization {loc_id}")
269
+ return
270
+ print(f" Updated group localization (ID: {loc_id})")
271
+
272
+
207
273
  def print_api_errors(resp, action: str) -> None:
208
274
  """Print human-readable API error messages."""
209
275
  try:
@@ -26,12 +26,12 @@ from asc_iap_api import BASE_URL, TIMEOUT, print_api_errors
26
26
  def get_subscription_availability(headers: dict, sub_id: str) -> dict | None:
27
27
  """Fetch current availability settings for a subscription.
28
28
 
29
- Includes availableTerritories relationship so callers can check territory count.
29
+ Returns the availability resource or None if not yet configured.
30
+ Does not include territory details to avoid ASC API limit errors.
30
31
  """
31
32
  resp = requests.get(
32
33
  f"{BASE_URL}/subscriptions/{sub_id}/subscriptionAvailability",
33
34
  headers=headers,
34
- params={"include": "availableTerritories", "limit[availableTerritories]": 200},
35
35
  timeout=TIMEOUT,
36
36
  )
37
37
  if not resp.ok:
@@ -39,11 +39,7 @@ def get_subscription_availability(headers: dict, sub_id: str) -> dict | None:
39
39
  return None
40
40
  print_api_errors(resp, f"get availability for subscription {sub_id}")
41
41
  return None
42
- body = resp.json()
43
- result = body.get("data")
44
- if result:
45
- result["_included_territories"] = body.get("included", [])
46
- return result
42
+ return resp.json().get("data")
47
43
 
48
44
 
49
45
  def list_all_territory_ids(headers: dict) -> list[str]:
@@ -21,14 +21,17 @@ import os
21
21
  import sys
22
22
 
23
23
  from asc_iap_api import (
24
+ create_group_localization,
24
25
  create_localization,
25
26
  create_subscription,
26
27
  create_subscription_group,
27
28
  get_app_id,
29
+ get_group_localizations,
28
30
  get_jwt_token,
29
31
  get_subscription_localizations,
30
32
  list_subscription_groups,
31
33
  list_subscriptions_in_group,
34
+ update_group_localization,
32
35
  update_localization,
33
36
  )
34
37
  from asc_subscription_setup import (
@@ -104,6 +107,27 @@ def sync_subscription_group(
104
107
  print(f"\nProcessing subscription group: {ref_name}")
105
108
  group_id = find_or_create_group(headers, app_id, ref_name, existing_groups)
106
109
 
110
+ # Sync group localizations
111
+ group_localizations = group_config.get("localizations", {})
112
+ if group_localizations:
113
+ existing_locs = get_group_localizations(headers, group_id)
114
+ existing_map = {loc["attributes"]["locale"]: loc for loc in existing_locs}
115
+
116
+ for locale, loc_data in group_localizations.items():
117
+ name = loc_data.get("name", "")
118
+ custom_name = loc_data.get("custom_name")
119
+
120
+ if locale in existing_map:
121
+ update_group_localization(
122
+ headers, existing_map[locale]["id"], name, custom_name
123
+ )
124
+ print(f" Updated group localization: {locale}")
125
+ else:
126
+ create_group_localization(
127
+ headers, group_id, locale, name, custom_name
128
+ )
129
+ print(f" Created group localization: {locale}")
130
+
107
131
  existing_subs = list_subscriptions_in_group(headers, group_id)
108
132
  sub_results = []
109
133
 
@@ -122,10 +146,14 @@ def sync_subscription_group(
122
146
  def _sync_availability(headers: dict, sub_id: str, sub_config: dict) -> None:
123
147
  """Ensure subscription territory availability is configured with all territories.
124
148
 
125
- If no territories are specified in config, fetches all App Store territories
126
- so the subscription is available everywhere (empty list = removed from sale).
127
- Also re-creates availability if existing territory count is suspiciously low.
149
+ If availability already exists, it is left as-is (idempotent skip).
150
+ Otherwise fetches all App Store territories and creates availability.
128
151
  """
152
+ existing = get_subscription_availability(headers, sub_id)
153
+ if existing:
154
+ print(" Availability already configured")
155
+ return
156
+
129
157
  avail_config = sub_config.get("availability", {})
130
158
  available_in_new = avail_config.get("available_in_new_territories", True)
131
159
  territory_ids = avail_config.get("territories", [])
@@ -137,14 +165,6 @@ def _sync_availability(headers: dict, sub_id: str, sub_config: dict) -> None:
137
165
  print(" WARNING: Could not fetch territories", file=sys.stderr)
138
166
  return
139
167
 
140
- existing = get_subscription_availability(headers, sub_id)
141
- if existing:
142
- included = existing.get("_included_territories", [])
143
- if len(included) >= 100:
144
- print(f" Availability OK ({len(included)} territories)")
145
- return
146
- print(f" Availability has only {len(included)} territories, recreating with {len(territory_ids)}")
147
-
148
168
  result = create_subscription_availability(
149
169
  headers, sub_id, territory_ids, available_in_new=available_in_new,
150
170
  )