@bprotsyk/aso-core 2.1.195 → 2.1.196
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/lib/app/app.d.ts +188 -181
- package/lib/app/app.js +60 -114
- package/lib/general/domain.d.ts +3 -3
- package/lib/index.d.ts +1 -7
- package/lib/index.js +6 -44
- package/lib/keitaro/keitaro-campaign.d.ts +1 -0
- package/lib/network/keitaro/http.js +1 -1
- package/lib/network/keitaro/keitaro-service.d.ts +25 -38
- package/lib/network/keitaro/keitaro-service.js +105 -450
- package/package.json +1 -1
- package/src/app/app.ts +96 -234
- package/src/index.ts +16 -10
- package/src/keitaro/keitaro-campaign.ts +1 -0
- package/src/network/keitaro/http.ts +1 -1
- package/src/network/keitaro/keitaro-service.ts +131 -545
- package/src/keitaro/keitaro-clo-geos.ts +0 -389
- package/src/network/keitaro/traffle/traffle-keitaro-service.ts +0 -1576
- package/src/panel/app/upsert-flash-app-request.ts +0 -36
- package/src/utils/keitaro-utils.ts +0 -853
- package/src/utils/traffle-keitaro-utils.ts +0 -45
|
@@ -1,389 +0,0 @@
|
|
|
1
|
-
import keitaroApi from "../network/keitaro/traffle/http";
|
|
2
|
-
import { IKeitaroCampaign } from "./keitaro-campaign";
|
|
3
|
-
import { IKeitaroStream } from "./keitaro-stream";
|
|
4
|
-
import { IApp, IPlatformParams, EPlatform } from "../app/app";
|
|
5
|
-
|
|
6
|
-
// Хелпер для нормалізації списку гео
|
|
7
|
-
function normalizeGeos(geos: string[] | undefined | null): string[] {
|
|
8
|
-
if (!Array.isArray(geos)) return [];
|
|
9
|
-
const unique = new Set<string>();
|
|
10
|
-
for (const raw of geos) {
|
|
11
|
-
if (typeof raw !== 'string') continue;
|
|
12
|
-
const val = raw.trim().toUpperCase();
|
|
13
|
-
if (!val) continue;
|
|
14
|
-
unique.add(val);
|
|
15
|
-
}
|
|
16
|
-
return Array.from(unique).sort();
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
// Інтерфейс для результату синхронізації гео
|
|
20
|
-
export interface IGeoSyncResult {
|
|
21
|
-
appId: number;
|
|
22
|
-
platform: string;
|
|
23
|
-
keitaroGeos: string[];
|
|
24
|
-
appGeos: string[];
|
|
25
|
-
missingGeos: string[];
|
|
26
|
-
needsUpdate: boolean;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// Інтерфейс для оновлення гео в додатку
|
|
30
|
-
export interface IGeoUpdateData {
|
|
31
|
-
appId: number;
|
|
32
|
-
platform: string;
|
|
33
|
-
newGeos: string[];
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// Інтерфейс для загального результату синхронізації
|
|
37
|
-
export interface IGeoSyncSummary {
|
|
38
|
-
totalApps: number;
|
|
39
|
-
appsNeedingUpdate: number;
|
|
40
|
-
updates: IGeoUpdateData[];
|
|
41
|
-
errors: string[];
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Отримує всі кампанії з Keitaro
|
|
46
|
-
*/
|
|
47
|
-
async function getAllKeitaroCampaigns(): Promise<IKeitaroCampaign[]> {
|
|
48
|
-
try {
|
|
49
|
-
const { data: campaigns } = await keitaroApi.get<IKeitaroCampaign[]>('campaigns');
|
|
50
|
-
return campaigns;
|
|
51
|
-
} catch (error) {
|
|
52
|
-
console.error('Помилка отримання кампаній з Keitaro:', error);
|
|
53
|
-
throw error;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Отримує всі потоки для конкретної кампанії
|
|
59
|
-
*/
|
|
60
|
-
async function getStreamsByCampaignId(campaignId: number): Promise<IKeitaroStream[]> {
|
|
61
|
-
try {
|
|
62
|
-
const { data: streams } = await keitaroApi.get<IKeitaroStream[]>(`campaigns/${campaignId}/streams`);
|
|
63
|
-
return streams;
|
|
64
|
-
} catch (error) {
|
|
65
|
-
console.error(`Помилка отримання потоків для кампанії ${campaignId}:`, error);
|
|
66
|
-
throw error;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Отримує гео з CLO потоку кампанії
|
|
72
|
-
*/
|
|
73
|
-
async function getCLOGeosFromCampaign(campaignId: number): Promise<string[]> {
|
|
74
|
-
try {
|
|
75
|
-
const streams = await getStreamsByCampaignId(campaignId);
|
|
76
|
-
console.log(`Отримано ${streams.length} потоків для кампанії ${campaignId}`);
|
|
77
|
-
|
|
78
|
-
const cloStream = streams.find(stream => stream.name === "CLO");
|
|
79
|
-
if (!cloStream) {
|
|
80
|
-
console.log(`CLO потік не знайдено для кампанії ${campaignId}`);
|
|
81
|
-
return [];
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
console.log(`Знайдено CLO потік:`, {
|
|
85
|
-
id: cloStream.id,
|
|
86
|
-
name: cloStream.name,
|
|
87
|
-
filters: cloStream.filters
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
if (!cloStream.filters || cloStream.filters.length === 0) {
|
|
91
|
-
console.log(`CLO потік не має фільтрів для кампанії ${campaignId}`);
|
|
92
|
-
return [];
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// Шукаємо фільтр за країною в CLO потоці
|
|
96
|
-
const countryFilter = cloStream.filters.find(filter => filter.name === "country");
|
|
97
|
-
if (!countryFilter) {
|
|
98
|
-
console.log(`Фільтр country не знайдено в CLO потоці кампанії ${campaignId}`);
|
|
99
|
-
return [];
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
console.log(`Знайдено фільтр country:`, countryFilter);
|
|
103
|
-
|
|
104
|
-
if (!countryFilter.payload || !Array.isArray(countryFilter.payload)) {
|
|
105
|
-
console.log(`Фільтр country не має payload або payload не є масивом для кампанії ${campaignId}`);
|
|
106
|
-
return [];
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
console.log(`Гео з CLO потоку кампанії ${campaignId}:`, countryFilter.payload);
|
|
110
|
-
return countryFilter.payload;
|
|
111
|
-
|
|
112
|
-
} catch (error) {
|
|
113
|
-
console.error(`Помилка отримання CLO гео для кампанії ${campaignId}:`, error);
|
|
114
|
-
return [];
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* Визначає платформу за назвою кампанії
|
|
120
|
-
*/
|
|
121
|
-
function detectPlatformFromCampaignName(campaignName: string): string {
|
|
122
|
-
const name = campaignName.toLowerCase();
|
|
123
|
-
|
|
124
|
-
if (name.includes('(hw)') || name.includes('huawei')) {
|
|
125
|
-
return 'hw';
|
|
126
|
-
} else if (name.includes('(sm)') || name.includes('samsung')) {
|
|
127
|
-
return 'sm';
|
|
128
|
-
} else if (name.includes('(rs)') || name.includes('rustore')) {
|
|
129
|
-
return 'rs';
|
|
130
|
-
} else if (name.includes('(xm)') || name.includes('xiaomi')) {
|
|
131
|
-
return 'xm';
|
|
132
|
-
} else if (name.includes('(ap)') || name.includes('apkpure')) {
|
|
133
|
-
return 'ap';
|
|
134
|
-
} else if (name.includes('(tg)') || name.includes('telegram')) {
|
|
135
|
-
return 'tg';
|
|
136
|
-
} else if (name.includes('(am)') || name.includes('amazon')) {
|
|
137
|
-
return 'am';
|
|
138
|
-
} else {
|
|
139
|
-
return '@'; // General platform
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Знаходить додаток за group кампанії
|
|
145
|
-
*/
|
|
146
|
-
function findAppByGroup(group: string, apps: IApp[]): IApp | null {
|
|
147
|
-
const groupId = parseInt(group);
|
|
148
|
-
if (isNaN(groupId)) return null;
|
|
149
|
-
return apps.find(app => app.id === groupId) || null;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* Основна функція синхронізації гео між Keitaro та додатками
|
|
154
|
-
*/
|
|
155
|
-
export async function syncKeitaroCLOGeosWithApps(apps: IApp[]): Promise<IGeoSyncSummary> {
|
|
156
|
-
console.log(`Починаємо синхронізацію гео для ${apps.length} додатків...`);
|
|
157
|
-
|
|
158
|
-
const summary: IGeoSyncSummary = {
|
|
159
|
-
totalApps: apps.length,
|
|
160
|
-
appsNeedingUpdate: 0,
|
|
161
|
-
updates: [],
|
|
162
|
-
errors: []
|
|
163
|
-
};
|
|
164
|
-
|
|
165
|
-
try {
|
|
166
|
-
// Отримуємо всі кампанії з Keitaro
|
|
167
|
-
const allCampaigns = await getAllKeitaroCampaigns();
|
|
168
|
-
console.log(`Отримано ${allCampaigns.length} кампаній з Keitaro`);
|
|
169
|
-
|
|
170
|
-
// Діагностика: виводимо всі кампанії з group
|
|
171
|
-
console.log('\n=== Діагностика кампаній ===');
|
|
172
|
-
const campaignsWithGroup = allCampaigns.filter(c => c.group);
|
|
173
|
-
console.log(`Кампанії з group: ${campaignsWithGroup.length}`);
|
|
174
|
-
|
|
175
|
-
// Виводимо перші 10 кампаній з group для діагностики
|
|
176
|
-
campaignsWithGroup.slice(0, 10).forEach(campaign => {
|
|
177
|
-
console.log(` ID: ${campaign.id}, Group: "${campaign.group}", Name: "${campaign.name}"`);
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
if (campaignsWithGroup.length > 10) {
|
|
181
|
-
console.log(` ... та ще ${campaignsWithGroup.length - 10} кампаній`);
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
// Діагностика: шукаємо кампанії з нашими bundle
|
|
185
|
-
console.log('\n=== Пошук кампаній за bundle ===');
|
|
186
|
-
apps.forEach(app => {
|
|
187
|
-
const campaignsWithBundle = allCampaigns.filter(c =>
|
|
188
|
-
c.name.toLowerCase().includes(app.bundle.toLowerCase())
|
|
189
|
-
);
|
|
190
|
-
console.log(`Кампанії з bundle "${app.bundle}": ${campaignsWithBundle.length}`);
|
|
191
|
-
campaignsWithBundle.forEach(campaign => {
|
|
192
|
-
console.log(` ID: ${campaign.id}, Group: "${campaign.group}", Name: "${campaign.name}"`);
|
|
193
|
-
});
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
// Створюємо мапу додатків за ID для швидкого пошуку
|
|
197
|
-
const appsMap = new Map<number, IApp>();
|
|
198
|
-
apps.forEach(app => appsMap.set(app.id, app));
|
|
199
|
-
|
|
200
|
-
console.log(`\nШукаємо додатки з ID: ${Array.from(appsMap.keys()).join(', ')}`);
|
|
201
|
-
|
|
202
|
-
// Фільтруємо кампанії, які мають group відповідний до наших додатків
|
|
203
|
-
const ourCampaigns = allCampaigns.filter(campaign => {
|
|
204
|
-
const hasGroup = campaign.group && appsMap.has(parseInt(campaign.group));
|
|
205
|
-
if (hasGroup) {
|
|
206
|
-
console.log(`✅ Знайдено нашу кампанію за group: ID=${campaign.id}, name="${campaign.name}", group="${campaign.group}"`);
|
|
207
|
-
}
|
|
208
|
-
return hasGroup;
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
// Додатково шукаємо кампанії за bundle в назві
|
|
212
|
-
const campaignsByBundle = allCampaigns.filter(campaign => {
|
|
213
|
-
return apps.some(app =>
|
|
214
|
-
campaign.name.toLowerCase().includes(app.bundle.toLowerCase())
|
|
215
|
-
);
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
console.log(`\nЗнайдено ${campaignsByBundle.length} кампаній за bundle`);
|
|
219
|
-
campaignsByBundle.forEach(campaign => {
|
|
220
|
-
const matchingApp = apps.find(app =>
|
|
221
|
-
campaign.name.toLowerCase().includes(app.bundle.toLowerCase())
|
|
222
|
-
);
|
|
223
|
-
if (matchingApp) {
|
|
224
|
-
console.log(`✅ Знайдено кампанію за bundle: ID=${campaign.id}, name="${campaign.name}", bundle="${matchingApp.bundle}"`);
|
|
225
|
-
}
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
// Об'єднуємо результати
|
|
229
|
-
const allOurCampaigns = [...ourCampaigns, ...campaignsByBundle];
|
|
230
|
-
const uniqueCampaigns = allOurCampaigns.filter((campaign, index, self) =>
|
|
231
|
-
index === self.findIndex(c => c.id === campaign.id)
|
|
232
|
-
);
|
|
233
|
-
|
|
234
|
-
console.log(`\nВсього знайдено ${uniqueCampaigns.length} унікальних кампаній наших додатків`);
|
|
235
|
-
|
|
236
|
-
// Обробляємо кожну кампанію
|
|
237
|
-
for (const campaign of uniqueCampaigns) {
|
|
238
|
-
try {
|
|
239
|
-
const group = campaign.group;
|
|
240
|
-
if (!group) continue;
|
|
241
|
-
|
|
242
|
-
const app = appsMap.get(parseInt(group));
|
|
243
|
-
if (!app) {
|
|
244
|
-
console.warn(`Додаток з ID ${group} не знайдено в списку додатків`);
|
|
245
|
-
continue;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
console.log(`\nОбробляємо кампанію ${campaign.id} для додатку ${group}:`);
|
|
249
|
-
console.log(` Назва: ${campaign.name}`);
|
|
250
|
-
console.log(` Group: ${campaign.group}`);
|
|
251
|
-
|
|
252
|
-
// Визначаємо платформу за назвою кампанії
|
|
253
|
-
const platform = detectPlatformFromCampaignName(campaign.name);
|
|
254
|
-
console.log(` Визначена платформа: ${platform}`);
|
|
255
|
-
|
|
256
|
-
// Отримуємо гео з CLO потоку кампанії
|
|
257
|
-
console.log(` Отримуємо гео з CLO потоку...`);
|
|
258
|
-
const keitaroGeosRaw = await getCLOGeosFromCampaign(campaign.id);
|
|
259
|
-
const keitaroGeos = normalizeGeos(keitaroGeosRaw);
|
|
260
|
-
console.log(` Keitaro CLO гео: ${keitaroGeos.join(', ')}`);
|
|
261
|
-
|
|
262
|
-
// Отримуємо гео з додатку для конкретної платформи
|
|
263
|
-
const platformData = app.platforms[platform as EPlatform];
|
|
264
|
-
const appGeos: string[] = []; // geo field removed from IPlatformParams
|
|
265
|
-
console.log(` App гео для платформи ${platform}: ${appGeos.join(', ')}`);
|
|
266
|
-
|
|
267
|
-
// Порівнюємо гео
|
|
268
|
-
const missingGeos = keitaroGeos.filter(geo => !appGeos.includes(geo));
|
|
269
|
-
const extraGeos = appGeos.filter(geo => !keitaroGeos.includes(geo));
|
|
270
|
-
const needsUpdate = missingGeos.length > 0 || extraGeos.length > 0;
|
|
271
|
-
|
|
272
|
-
console.log(` Відсутні гео в app: ${missingGeos.join(', ')}`);
|
|
273
|
-
console.log(` Потребує оновлення: ${needsUpdate}`);
|
|
274
|
-
|
|
275
|
-
if (needsUpdate) {
|
|
276
|
-
summary.appsNeedingUpdate++;
|
|
277
|
-
|
|
278
|
-
// Формуємо нові гео - беремо тільки ті, що є в Keitaro
|
|
279
|
-
const updateData: IGeoUpdateData = {
|
|
280
|
-
appId: parseInt(group),
|
|
281
|
-
platform: platform,
|
|
282
|
-
newGeos: keitaroGeos // Використовуємо гео з Keitaro як джерело істини
|
|
283
|
-
};
|
|
284
|
-
|
|
285
|
-
summary.updates.push(updateData);
|
|
286
|
-
|
|
287
|
-
console.log(`✅ Додаток ${group} (${platform}) потребує оновлення гео:`);
|
|
288
|
-
console.log(` Кампанія: ${campaign.name}`);
|
|
289
|
-
console.log(` Keitaro гео: ${keitaroGeos.join(', ')}`);
|
|
290
|
-
console.log(` App гео: ${appGeos.join(', ')}`);
|
|
291
|
-
console.log(` Відсутні гео в app: ${missingGeos.join(', ')}`);
|
|
292
|
-
console.log(` Зайві гео в app: ${extraGeos.join(', ')}`);
|
|
293
|
-
console.log(` Нові гео (з Keitaro): ${updateData.newGeos.join(', ')}`);
|
|
294
|
-
} else {
|
|
295
|
-
console.log(`✅ Додаток ${group} (${platform}) не потребує оновлення гео - гео ідентичні`);
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
} catch (error) {
|
|
299
|
-
const errorMsg = `Помилка обробки кампанії ${campaign.id} (${campaign.name}): ${error}`;
|
|
300
|
-
console.error(errorMsg);
|
|
301
|
-
summary.errors.push(errorMsg);
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
console.log(`\nСинхронізація завершена. ${summary.appsNeedingUpdate} додатків потребують оновлення гео.`);
|
|
306
|
-
|
|
307
|
-
} catch (error) {
|
|
308
|
-
const errorMsg = `Критична помилка синхронізації: ${error}`;
|
|
309
|
-
console.error(errorMsg);
|
|
310
|
-
summary.errors.push(errorMsg);
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
return summary;
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
/**
|
|
317
|
-
* Отримує детальну інформацію про синхронізацію для конкретного додатку
|
|
318
|
-
*/
|
|
319
|
-
export async function getAppGeoSyncDetails(appId: number, platform: string, apps: IApp[]): Promise<IGeoSyncResult | null> {
|
|
320
|
-
try {
|
|
321
|
-
const app = apps.find(a => a.id === appId);
|
|
322
|
-
if (!app) {
|
|
323
|
-
console.error(`Додаток з ID ${appId} не знайдено`);
|
|
324
|
-
return null;
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
const platformData = app.platforms[platform as EPlatform];
|
|
328
|
-
if (!platformData) {
|
|
329
|
-
console.error(`Платформа ${platform} не знайдена для додатку ${appId}`);
|
|
330
|
-
return null;
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
// Отримуємо всі кампанії та шукаємо нашу за group
|
|
334
|
-
const allCampaigns = await getAllKeitaroCampaigns();
|
|
335
|
-
const ourCampaign = allCampaigns.find(campaign =>
|
|
336
|
-
campaign.group === appId.toString()
|
|
337
|
-
);
|
|
338
|
-
|
|
339
|
-
if (!ourCampaign) {
|
|
340
|
-
console.error(`Кампанія для додатку ${appId} (group: ${appId}) не знайдена в Keitaro`);
|
|
341
|
-
return null;
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
console.log(`Знайдено кампанію для додатку ${appId}:`, {
|
|
345
|
-
id: ourCampaign.id,
|
|
346
|
-
name: ourCampaign.name,
|
|
347
|
-
group: ourCampaign.group
|
|
348
|
-
});
|
|
349
|
-
|
|
350
|
-
// Отримуємо гео з CLO потоку
|
|
351
|
-
const keitaroGeos = await getCLOGeosFromCampaign(ourCampaign.id);
|
|
352
|
-
const appGeos: string[] = []; // geo field removed from IPlatformParams
|
|
353
|
-
|
|
354
|
-
// Порівнюємо гео
|
|
355
|
-
const missingGeos = keitaroGeos.filter((geo: string) => !appGeos.includes(geo));
|
|
356
|
-
const needsUpdate = missingGeos.length > 0 || appGeos.length !== keitaroGeos.length ||
|
|
357
|
-
!appGeos.every((geo: string) => keitaroGeos.includes(geo));
|
|
358
|
-
|
|
359
|
-
return {
|
|
360
|
-
appId,
|
|
361
|
-
platform,
|
|
362
|
-
keitaroGeos,
|
|
363
|
-
appGeos,
|
|
364
|
-
missingGeos,
|
|
365
|
-
needsUpdate
|
|
366
|
-
};
|
|
367
|
-
|
|
368
|
-
} catch (error) {
|
|
369
|
-
console.error(`Помилка отримання деталей синхронізації для додатку ${appId}:`, error);
|
|
370
|
-
return null;
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
/**
|
|
375
|
-
* Отримує всі додатки, які потребують оновлення гео
|
|
376
|
-
*/
|
|
377
|
-
export async function getAppsNeedingGeoUpdate(apps: IApp[]): Promise<IGeoUpdateData[]> {
|
|
378
|
-
const summary = await syncKeitaroCLOGeosWithApps(apps);
|
|
379
|
-
return summary.updates;
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
/**
|
|
383
|
-
* Експортуємо основні функції
|
|
384
|
-
*/
|
|
385
|
-
export const KeitaroCLOGeosService = {
|
|
386
|
-
syncKeitaroCLOGeosWithApps,
|
|
387
|
-
getAppGeoSyncDetails,
|
|
388
|
-
getAppsNeedingGeoUpdate
|
|
389
|
-
};
|