play products: clamp regional prices to Play's min/max, per-product results; apple iap: skip live localizations, per-product results
This commit is contained in:
parent
882255a32f
commit
b0641c1fc8
12 changed files with 456 additions and 69 deletions
|
|
@ -18,6 +18,7 @@
|
|||
import { existsSync, readFileSync } from 'node:fs';
|
||||
import { type OfferCodeDef, sellsOn, type Subscription, type SubscriptionGroup } from '../lib/config';
|
||||
import type { Ctx } from '../lib/ctx';
|
||||
import { isLockedLocState, isUnmodifiableError, lockedLocNote } from '../lib/loc-lock';
|
||||
import { confirm, need } from '../lib/ctx';
|
||||
import { done, log, step, warn } from '../lib/log';
|
||||
import { allowPriceChanges, type HeldPrice, heldSection, holdPriceChanges, withoutYes } from '../lib/price-gate';
|
||||
|
|
@ -73,7 +74,7 @@ const heldOf = (id: string, changes: { territory: string; from: number | null; t
|
|||
/** Everything an existing subscription has that differs from the config (pure data; the run prints and applies it). */
|
||||
export interface SubDrift {
|
||||
attrs: AppleAttrDiff;
|
||||
locs: { id: string; loc: string; patch: Record<string, string> }[];
|
||||
locs: { id: string; loc: string; patch: Record<string, string>; locked?: string }[];
|
||||
availability: { missing: string[]; newTerritories: boolean } | null;
|
||||
intro: string[];
|
||||
}
|
||||
|
|
@ -88,7 +89,7 @@ export async function readSubDrift(c: Asc, s: Subscription, id: string, locale:
|
|||
const loc = locale.get(lang);
|
||||
const h = loc ? haveLocs.find((x: J) => x.attributes.locale === loc) : undefined;
|
||||
const patch = h ? appleLocDiff(l, { name: h.attributes.name, description: h.attributes.description }) : null;
|
||||
if (h && patch && loc) locs.push({ id: h.id, loc, patch });
|
||||
if (h && patch && loc) locs.push({ id: h.id, loc, patch, ...(isLockedLocState(h.attributes.state) ? { locked: h.attributes.state as string } : {}) });
|
||||
}
|
||||
let availability: SubDrift['availability'] = null;
|
||||
const av = (await c.maybe(`/v1/subscriptions/${id}/subscriptionAvailability`))?.data;
|
||||
|
|
@ -103,14 +104,14 @@ export async function readSubDrift(c: Asc, s: Subscription, id: string, locale:
|
|||
}
|
||||
|
||||
/** Group localizations whose name / customAppName differ from the config. Read-only. */
|
||||
export async function readGroupLocDrift(c: Asc, g: SubscriptionGroup, groupId: string, locale: Map<string, string | undefined>): Promise<{ id: string; loc: string; patch: Record<string, string> }[]> {
|
||||
export async function readGroupLocDrift(c: Asc, g: SubscriptionGroup, groupId: string, locale: Map<string, string | undefined>): Promise<{ id: string; loc: string; patch: Record<string, string>; locked?: string }[]> {
|
||||
const have = await c.all(`/v1/subscriptionGroups/${groupId}/subscriptionGroupLocalizations?limit=200`);
|
||||
const out: { id: string; loc: string; patch: Record<string, string> }[] = [];
|
||||
const out: { id: string; loc: string; patch: Record<string, string>; locked?: string }[] = [];
|
||||
for (const [lang, l] of Object.entries(g.listings)) {
|
||||
const loc = locale.get(lang);
|
||||
const h = loc ? have.find((x: J) => x.attributes.locale === loc) : undefined;
|
||||
const patch = h ? appleGroupLocDiff(l, { name: h.attributes.name, customAppName: h.attributes.customAppName }) : null;
|
||||
if (h && patch && loc) out.push({ id: h.id, loc, patch });
|
||||
if (h && patch && loc) out.push({ id: h.id, loc, patch, ...(isLockedLocState(h.attributes.state) ? { locked: h.attributes.state as string } : {}) });
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
|
@ -120,7 +121,7 @@ export function driftLines(id: string, d: SubDrift): string[] {
|
|||
const av = d.availability;
|
||||
return [
|
||||
...d.attrs.text.map(x => `${id}: ${x}`), ...d.attrs.report,
|
||||
...d.locs.map(l => `${id} ${l.loc}: ${Object.keys(l.patch).join(' + ')} differ from the config`),
|
||||
...d.locs.map(l => `${id} ${l.loc}: ${Object.keys(l.patch).join(' + ')} differ from the config${l.locked ? ` - ${lockedLocNote(l.locked)}` : ''}`),
|
||||
...(av ? [`${id}: availability${av.missing.length ? ` ${av.missing.length} territories missing (${av.missing.slice(0, 5).join(', ')})` : ''}${av.newTerritories ? ' new territories off' : ''}`] : []),
|
||||
...d.intro,
|
||||
];
|
||||
|
|
@ -160,7 +161,7 @@ export async function run(ctx: Ctx): Promise<void> {
|
|||
const heldDry: HeldPrice[] = [];
|
||||
for (const g of groups) {
|
||||
const sg = state.groups.find(x => x.referenceName === g.name);
|
||||
if (sg) for (const d of await readGroupLocDrift(c, g, sg.id, locale)) log(TAG, `group "${g.name}" ${d.loc}: ${Object.keys(d.patch).join(' + ')} differ from the config`);
|
||||
if (sg) for (const d of await readGroupLocDrift(c, g, sg.id, locale)) log(TAG, `group "${g.name}" ${d.loc}: ${Object.keys(d.patch).join(' + ')} differ from the config${d.locked ? ` - ${lockedLocNote(d.locked)}` : ''}`);
|
||||
for (const s of g.subscriptions) {
|
||||
const cur = state.subscriptions.find(x => x.productId === s.id);
|
||||
if (!cur) continue;
|
||||
|
|
@ -209,7 +210,11 @@ export async function run(ctx: Ctx): Promise<void> {
|
|||
}
|
||||
if (state.groups.some(x => x.id === groupId)) {
|
||||
for (const d of await readGroupLocDrift(c, g, groupId, locale)) {
|
||||
await c.asc('PATCH', `/v1/subscriptionGroupLocalizations/${d.id}`, appleGroupLocPatchBody(d.id, d.patch));
|
||||
if (d.locked) { warn(TAG, ` group ${d.loc}: ${lockedLocNote(d.locked)}; skipped`); continue; }
|
||||
try { await c.asc('PATCH', `/v1/subscriptionGroupLocalizations/${d.id}`, appleGroupLocPatchBody(d.id, d.patch)); } catch (e) {
|
||||
if (!isUnmodifiableError(e)) throw e;
|
||||
warn(TAG, ` group ${d.loc}: ${lockedLocNote('ACTIVE')}; skipped`); continue;
|
||||
}
|
||||
log(TAG, ` group ${d.loc}: ${Object.keys(d.patch).join(' + ')} updated`);
|
||||
}
|
||||
}
|
||||
|
|
@ -242,7 +247,14 @@ export async function run(ctx: Ctx): Promise<void> {
|
|||
// and the period / family sharing are only reported (Apple cannot change them)
|
||||
const d = await readSubDrift(c, s, subId, locale, territories);
|
||||
if (Object.keys(d.attrs.patch).length) { await c.asc('PATCH', `/v1/subscriptions/${subId}`, appleSubPatchBody(subId, d.attrs.patch)); log(TAG, ` attributes updated: ${d.attrs.text.join(', ')}`); }
|
||||
for (const l of d.locs) { await c.asc('PATCH', `/v1/subscriptionLocalizations/${l.id}`, appleLocPatchBody(l.id, l.patch)); log(TAG, ` ${l.loc}: ${Object.keys(l.patch).join(' + ')} updated`); }
|
||||
for (const l of d.locs) {
|
||||
if (l.locked) { warn(TAG, ` ${l.loc}: ${lockedLocNote(l.locked)}; skipped`); continue; }
|
||||
try { await c.asc('PATCH', `/v1/subscriptionLocalizations/${l.id}`, appleLocPatchBody(l.id, l.patch)); } catch (e) {
|
||||
if (!isUnmodifiableError(e)) throw e;
|
||||
warn(TAG, ` ${l.loc}: ${lockedLocNote('ACTIVE')}; skipped`); continue;
|
||||
}
|
||||
log(TAG, ` ${l.loc}: ${Object.keys(l.patch).join(' + ')} updated`);
|
||||
}
|
||||
if (d.availability) { await c.asc('POST', '/v1/subscriptionAvailabilities', appleAvailabilityBody(subId, territories)); log(TAG, ` availability set again: ${territories.length} territories + new ones`); }
|
||||
for (const x of [...d.attrs.report, ...d.intro]) warn(TAG, x);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue