Play console steps without an API as agent plans: play-store-settings (App category; play listing cannot set it), play-app-content (every card in order, one owner batch) and its cards (privacy policy, ads, sign in details, advertising ID, government, financial, health), play-production-countries and play-production-release; IARC plan with the Terms of Use agreement, follow-ups (console.contentRating.details, products[].randomRewards, social) and the Summary's final Save; config play.category / countries / managedPublishing, console.appContent; check, validate, play status and play release reminders; inPageLoop helper and material checkbox clicks; App Privacy published signal; first IAPs via apple iap-submit --with-version. Pitfalls 27, 28

This commit is contained in:
CRTkafa 2026-10-12 10:16:34 +08:00
parent dbd9f2973c
commit b7bfe89151
37 changed files with 1624 additions and 121 deletions

View file

@ -15,7 +15,7 @@
// bad behavior threshold is a blocker, near it a warning; a store without vitals access is only noted. --no-health skips.
import { ascClient, type J } from './apple/asc';
import { requiredDisplayTypes } from './apple/screenshots';
import { type Loaded, playCodes, profileOf, sellsOn } from './lib/config';
import { type Loaded, playCategoryOf, playCodes, profileOf, sellsOn } from './lib/config';
import { reviewDemoAccount } from './lib/credentials';
import { allSubscriptions, basePlanId } from './lib/subscriptions';
import type { Ctx } from './lib/ctx';
@ -132,6 +132,9 @@ export async function collectPlay(ctx: Ctx): Promise<PlaySnapshot> {
const tracks = ((await play.call<{ tracks?: { track: string; releases?: { status: string; versionCodes?: string[]; userFraction?: number }[] }[] }>('GET', `${play.API}/edits/${e.id}/tracks`)).tracks ?? [])
.map(t => ({ track: t.track, releases: (t.releases ?? []).map(r => ({ status: r.status, versionCodes: r.versionCodes ?? [], userFraction: r.userFraction })) }));
const products = (await listOneTimeProducts(play)).map(p => ({ productId: p.productId, states: (p.purchaseOptions ?? []).map(o => o.state ?? '') }));
// the production track's countries (read-only in the API: set in the console, `console plan play-production-countries`)
const productionCountries = await play.call<{ countries?: unknown[]; restOfWorld?: boolean }>('GET', `${play.API}/edits/${e.id}/countryAvailability/production`)
.then(r => (r.restOfWorld ? -1 : (r.countries ?? []).length)).catch(() => null);
const website = details.contactWebsite ?? null;
const appAdsLines = config.ads?.appAdsTxt ?? [];
const adsLikely = !!config.ads || Object.values(config.dataSafety?.data ?? {}).some(u => u.collect.includes('advertising'));
@ -155,7 +158,7 @@ export async function collectPlay(ctx: Ctx): Promise<PlaySnapshot> {
defaultLanguage: details.defaultLanguage ?? config.play?.defaultLanguage ?? 'en-US',
expectedLanguages: config.languages.flatMap(playCodes), listings, tracks, products,
expectedProducts: (config.products ?? []).filter(p => sellsOn(config, p, 'play')).map(p => p.id),
privacyUrl: await checkUrl(config.app.privacyPolicyUrl), website, appAds, appAdsLines, adsLikely,
privacyUrl: await checkUrl(config.app.privacyPolicyUrl), website, appAds, appAdsLines, adsLikely, category: playCategoryOf(config), productionCountries,
};
} finally {
await play.call('DELETE', `${play.API}/edits/${e.id}`).catch(() => {});