adapty 0.8.6 → 0.8.7
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.
- package/README.md +1 -0
- package/dist/commands/asa/keywords/recommend.d.ts +20 -0
- package/dist/commands/asa/keywords/recommend.js +102 -0
- package/dist/lib/asa-schemas.d.ts +40 -0
- package/oclif.manifest.json +816 -753
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -185,6 +185,7 @@ Keywords are always applied as a batch, at most 100 per call, and a partial reje
|
|
|
185
185
|
|
|
186
186
|
```sh
|
|
187
187
|
adapty asa keywords list
|
|
188
|
+
adapty asa keywords recommend --adam-id 1668337467 --type brand|generic|competitor [--country US...]
|
|
188
189
|
adapty asa keywords add --ad-group UUID --text "running shoes" --text "trail shoes" [--bid 1.20] [--match-type EXACT]
|
|
189
190
|
adapty asa keywords add --ad-group UUID --from-file keywords.txt
|
|
190
191
|
adapty asa keywords update KEYWORD_ID [KEYWORD_ID...] [--bid 2.00] [--status PAUSED]
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
import type { AsaBrandKeywordPoolDTO, AsaCompetitorBrandKeywordPoolsDTO, AsaGenericKeywordPoolDTO } from '../../../lib/asa-schemas.js';
|
|
3
|
+
type PoolDTO = AsaBrandKeywordPoolDTO | AsaGenericKeywordPoolDTO;
|
|
4
|
+
type RecommendationResult = AsaCompetitorBrandKeywordPoolsDTO | PoolDTO;
|
|
5
|
+
export default class AsaKeywordsRecommend extends Command {
|
|
6
|
+
static description: string;
|
|
7
|
+
static enableJsonFlag: boolean;
|
|
8
|
+
static examples: string[];
|
|
9
|
+
static flags: {
|
|
10
|
+
'adam-id': import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
country: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
type: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
+
};
|
|
14
|
+
run(): Promise<RecommendationResult>;
|
|
15
|
+
private printCompetitorPools;
|
|
16
|
+
private printPool;
|
|
17
|
+
private omitAdamId;
|
|
18
|
+
private statusHint;
|
|
19
|
+
}
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { Command, Flags } from '@oclif/core';
|
|
2
|
+
import { createAsaClient } from '../../../lib/asa-client.js';
|
|
3
|
+
import { printList, printResponse } from '../../../lib/output.js';
|
|
4
|
+
const TYPES = ['brand', 'generic', 'competitor'];
|
|
5
|
+
const PATHS = {
|
|
6
|
+
brand: '/keyword-recommendations/brand',
|
|
7
|
+
competitor: '/keyword-recommendations/competitor-brand',
|
|
8
|
+
generic: '/keyword-recommendations/generic',
|
|
9
|
+
};
|
|
10
|
+
export default class AsaKeywordsRecommend extends Command {
|
|
11
|
+
static description = 'Keyword recommendations for one of your apps: its brand terms, generic terms, or the brand terms of its selected competitors';
|
|
12
|
+
static enableJsonFlag = true;
|
|
13
|
+
static examples = [
|
|
14
|
+
'<%= config.bin %> asa keywords recommend --adam-id 1668337467 --type brand',
|
|
15
|
+
'<%= config.bin %> asa keywords recommend --adam-id 1668337467 --type generic --country US',
|
|
16
|
+
'<%= config.bin %> asa keywords recommend --adam-id 1668337467 --type competitor',
|
|
17
|
+
];
|
|
18
|
+
static flags = {
|
|
19
|
+
'adam-id': Flags.integer({
|
|
20
|
+
description: 'Apple App Store ID (adam_id) of your app, from `asa apps list`',
|
|
21
|
+
required: true,
|
|
22
|
+
}),
|
|
23
|
+
'country': Flags.string({
|
|
24
|
+
description: 'ISO country code (e.g. US); repeatable, default every country in the pool',
|
|
25
|
+
multiple: true,
|
|
26
|
+
}),
|
|
27
|
+
'type': Flags.string({ description: 'Which pool to read', options: [...TYPES], required: true }),
|
|
28
|
+
};
|
|
29
|
+
async run() {
|
|
30
|
+
const { flags } = await this.parse(AsaKeywordsRecommend);
|
|
31
|
+
const countries = (flags.country ?? []).map(code => code.trim().toUpperCase());
|
|
32
|
+
if (countries.some(code => !/^[A-Z]{2}$/.test(code))) {
|
|
33
|
+
this.error('--country takes two-letter ISO codes, e.g. --country US.', { exit: 2 });
|
|
34
|
+
}
|
|
35
|
+
const type = flags.type;
|
|
36
|
+
const client = await createAsaClient(this);
|
|
37
|
+
const params = { adam_id: String(flags['adam-id']), country: countries.length > 0 ? countries : undefined };
|
|
38
|
+
if (type === 'competitor') {
|
|
39
|
+
const pools = await client.get(PATHS.competitor, params);
|
|
40
|
+
this.printCompetitorPools(pools);
|
|
41
|
+
return pools;
|
|
42
|
+
}
|
|
43
|
+
const pool = await client.get(PATHS[type], params);
|
|
44
|
+
this.printPool(pool, type);
|
|
45
|
+
return pool;
|
|
46
|
+
}
|
|
47
|
+
printCompetitorPools(result) {
|
|
48
|
+
if (result.pools.length === 0) {
|
|
49
|
+
this.log('No competitors selected for this app yet — select them in the Adapty dashboard (Autopilot setup).');
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
for (const [i, pool] of result.pools.entries()) {
|
|
53
|
+
printResponse({
|
|
54
|
+
competitor_adam_id: pool.competitor_adam_id,
|
|
55
|
+
status: pool.status,
|
|
56
|
+
keywords_count: pool.keywords.length,
|
|
57
|
+
}, this.log.bind(this));
|
|
58
|
+
const hint = this.statusHint(pool.status);
|
|
59
|
+
if (hint !== undefined) {
|
|
60
|
+
this.log(hint);
|
|
61
|
+
}
|
|
62
|
+
if (pool.keywords.length > 0) {
|
|
63
|
+
this.log('');
|
|
64
|
+
printList(pool.keywords.map(keyword => this.omitAdamId(keyword)), this.log.bind(this));
|
|
65
|
+
}
|
|
66
|
+
if (i < result.pools.length - 1) {
|
|
67
|
+
this.log('===');
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
printPool(pool, type) {
|
|
72
|
+
const header = { status: pool.status, keywords_count: pool.keywords.length };
|
|
73
|
+
if (type === 'brand') {
|
|
74
|
+
header.brand_terms = pool.brand?.brand_terms ?? [];
|
|
75
|
+
}
|
|
76
|
+
printResponse(header, this.log.bind(this));
|
|
77
|
+
const hint = this.statusHint(pool.status);
|
|
78
|
+
if (hint !== undefined) {
|
|
79
|
+
this.log(hint);
|
|
80
|
+
}
|
|
81
|
+
if (pool.keywords.length > 0) {
|
|
82
|
+
this.log('');
|
|
83
|
+
const keywords = type === 'brand'
|
|
84
|
+
? pool.keywords.map(keyword => this.omitAdamId(keyword))
|
|
85
|
+
: pool.keywords;
|
|
86
|
+
printList(keywords, this.log.bind(this));
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
omitAdamId(keyword) {
|
|
90
|
+
const { adam_id: _adamId, ...rest } = keyword;
|
|
91
|
+
return rest;
|
|
92
|
+
}
|
|
93
|
+
statusHint(status) {
|
|
94
|
+
if (status === 'building') {
|
|
95
|
+
return 'Still building — retry in about a minute.';
|
|
96
|
+
}
|
|
97
|
+
if (status === 'failed') {
|
|
98
|
+
return 'The pool build failed — retry later or contact support.';
|
|
99
|
+
}
|
|
100
|
+
return undefined;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
@@ -379,3 +379,43 @@ export type AsaBulkConvertResultDTO = {
|
|
|
379
379
|
request: null | Record<string, unknown>;
|
|
380
380
|
warnings: AsaTemplateIssueDTO[];
|
|
381
381
|
};
|
|
382
|
+
export type AsaKeywordPoolStatus = 'building' | 'empty' | 'failed' | 'missing' | 'ready';
|
|
383
|
+
export type AsaBrandKeywordDTO = {
|
|
384
|
+
adam_id: number;
|
|
385
|
+
country: string;
|
|
386
|
+
median_organic_rank: null | number;
|
|
387
|
+
popularity: null | number;
|
|
388
|
+
score: null | number;
|
|
389
|
+
source: 'llm' | 'name' | 'organic';
|
|
390
|
+
text: string;
|
|
391
|
+
};
|
|
392
|
+
export type AsaAppBrandDTO = {
|
|
393
|
+
adam_id: number;
|
|
394
|
+
brand_terms: string[];
|
|
395
|
+
source: 'llm' | 'manual';
|
|
396
|
+
};
|
|
397
|
+
export type AsaBrandKeywordPoolDTO = {
|
|
398
|
+
brand: AsaAppBrandDTO | null;
|
|
399
|
+
keywords: AsaBrandKeywordDTO[];
|
|
400
|
+
status: AsaKeywordPoolStatus;
|
|
401
|
+
};
|
|
402
|
+
export type AsaGenericKeywordDTO = {
|
|
403
|
+
country: string;
|
|
404
|
+
median_organic_rank: null | number;
|
|
405
|
+
popularity: null | number;
|
|
406
|
+
relevance_tier: 'other' | 'top_organic';
|
|
407
|
+
score: null | number;
|
|
408
|
+
text: string;
|
|
409
|
+
};
|
|
410
|
+
export type AsaGenericKeywordPoolDTO = {
|
|
411
|
+
keywords: AsaGenericKeywordDTO[];
|
|
412
|
+
status: AsaKeywordPoolStatus;
|
|
413
|
+
};
|
|
414
|
+
export type AsaCompetitorBrandKeywordPoolDTO = {
|
|
415
|
+
competitor_adam_id: number;
|
|
416
|
+
keywords: AsaBrandKeywordDTO[];
|
|
417
|
+
status: AsaKeywordPoolStatus;
|
|
418
|
+
};
|
|
419
|
+
export type AsaCompetitorBrandKeywordPoolsDTO = {
|
|
420
|
+
pools: AsaCompetitorBrandKeywordPoolDTO[];
|
|
421
|
+
};
|