@bprotsyk/aso-core 2.1.114 → 2.1.116

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.
@@ -0,0 +1,212 @@
1
+ apple
2
+ banana
3
+ cherry
4
+ dragon
5
+ eagle
6
+ forest
7
+ garden
8
+ hammer
9
+ island
10
+ jungle
11
+ kitten
12
+ lemon
13
+ mountain
14
+ ocean
15
+ pencil
16
+ queen
17
+ river
18
+ sunset
19
+ tiger
20
+ umbrella
21
+ village
22
+ window
23
+ xylophone
24
+ yellow
25
+ zebra
26
+ adventure
27
+ beautiful
28
+ creative
29
+ delicious
30
+ excellent
31
+ fantastic
32
+ gorgeous
33
+ handsome
34
+ incredible
35
+ joyful
36
+ knowledge
37
+ luminous
38
+ magnificent
39
+ natural
40
+ outstanding
41
+ perfect
42
+ quality
43
+ radiant
44
+ spectacular
45
+ tremendous
46
+ ultimate
47
+ valuable
48
+ wonderful
49
+ amazing
50
+ brilliant
51
+ charming
52
+ delightful
53
+ elegant
54
+ fascinating
55
+ graceful
56
+ harmonious
57
+ inspiring
58
+ jubilant
59
+ kindhearted
60
+ loving
61
+ mysterious
62
+ nurturing
63
+ optimistic
64
+ peaceful
65
+ quintessential
66
+ radiant
67
+ serene
68
+ tranquil
69
+ uplifting
70
+ vibrant
71
+ whimsical
72
+ abundant
73
+ blessed
74
+ cheerful
75
+ dazzling
76
+ energetic
77
+ friendly
78
+ generous
79
+ hopeful
80
+ imaginative
81
+ jovial
82
+ knowledgeable
83
+ lively
84
+ majestic
85
+ noble
86
+ outstanding
87
+ passionate
88
+ quintessential
89
+ remarkable
90
+ splendid
91
+ treasured
92
+ unique
93
+ valuable
94
+ warmhearted
95
+ authentic
96
+ benevolent
97
+ charismatic
98
+ determined
99
+ enthusiastic
100
+ faithful
101
+ gracious
102
+ humble
103
+ inspiring
104
+ joyful
105
+ kind
106
+ loyal
107
+ magnificent
108
+ nurturing
109
+ optimistic
110
+ patient
111
+ quintessential
112
+ resilient
113
+ sincere
114
+ trustworthy
115
+ understanding
116
+ virtuous
117
+ wise
118
+ zealous
119
+ abundant
120
+ blessed
121
+ cheerful
122
+ dazzling
123
+ energetic
124
+ friendly
125
+ generous
126
+ hopeful
127
+ imaginative
128
+ jovial
129
+ knowledgeable
130
+ lively
131
+ majestic
132
+ noble
133
+ outstanding
134
+ passionate
135
+ quintessential
136
+ remarkable
137
+ splendid
138
+ treasured
139
+ unique
140
+ valuable
141
+ warmhearted
142
+ authentic
143
+ benevolent
144
+ charismatic
145
+ determined
146
+ enthusiastic
147
+ faithful
148
+ gracious
149
+ humble
150
+ inspiring
151
+ joyful
152
+ kind
153
+ loyal
154
+ magnificent
155
+ nurturing
156
+ optimistic
157
+ patient
158
+ quintessential
159
+ resilient
160
+ sincere
161
+ trustworthy
162
+ understanding
163
+ virtuous
164
+ wise
165
+ zealous
166
+ abundant
167
+ blessed
168
+ cheerful
169
+ dazzling
170
+ energetic
171
+ friendly
172
+ generous
173
+ hopeful
174
+ imaginative
175
+ jovial
176
+ knowledgeable
177
+ lively
178
+ majestic
179
+ noble
180
+ outstanding
181
+ passionate
182
+ quintessential
183
+ remarkable
184
+ splendid
185
+ treasured
186
+ unique
187
+ valuable
188
+ warmhearted
189
+ authentic
190
+ benevolent
191
+ charismatic
192
+ determined
193
+ enthusiastic
194
+ faithful
195
+ gracious
196
+ humble
197
+ inspiring
198
+ joyful
199
+ kind
200
+ loyal
201
+ magnificent
202
+ nurturing
203
+ optimistic
204
+ patient
205
+ quintessential
206
+ resilient
207
+ sincere
208
+ trustworthy
209
+ understanding
210
+ virtuous
211
+ wise
212
+ zealous
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "@bprotsyk/aso-core",
3
- "version": "2.1.114",
3
+ "version": "2.1.116",
4
4
  "main": "lib/index.js",
5
5
  "types": "lib/index.d.ts",
6
6
  "scripts": {
7
7
  "prod": "tsc -p ./tsconfig.json && copyfiles -u 1 src/templates/**/* lib/ && git add . && git commit -m \"Зміни до версії\" && git push origin master && npm version patch && npm publish",
8
- "build": "tsc",
8
+ "build": "tsc && copyfiles -u 1 src/templates/**/* lib/",
9
+ "copy-templates": "copyfiles -u 1 src/templates/**/* lib/",
9
10
  "test": "echo \"Error: no test specified\" && exit 1",
10
11
  "keitaro": "tsc; node lib/utils/keitaro-utils.js",
11
12
  "traffle": "tsc; node lib/utils/traffle-keitaro-utils.js",
package/src/index.ts CHANGED
@@ -32,4 +32,5 @@ export { IDomain, IDomainSetupResult, DomainStatus, DomainTarget, CONST_CLOUFLAR
32
32
  export { INamecheapDomain, INamecheapBuyRequest, INamecheapContactInfo, NamecheapBuyRequestSchema, INamecheapGetDomainsResult, INamecheapBuyResult } from "./general/namecheap-domain"
33
33
  export { NginxTemplate } from "./templates/nginx-template";
34
34
 
35
- export { TraffleKeitaroService } from "./network/keitaro/traffle/traffle-keitaro-service"
35
+ export { TraffleKeitaroService } from "./network/keitaro/traffle/traffle-keitaro-service"
36
+ export { KeitaroCLOGeosService } from "./keitaro/keitaro-clo-geos"
@@ -0,0 +1,371 @@
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
+ export interface IGeoSyncResult {
8
+ appId: number;
9
+ platform: string;
10
+ keitaroGeos: string[];
11
+ appGeos: string[];
12
+ missingGeos: string[];
13
+ needsUpdate: boolean;
14
+ }
15
+
16
+ // Інтерфейс для оновлення гео в додатку
17
+ export interface IGeoUpdateData {
18
+ appId: number;
19
+ platform: string;
20
+ newGeos: string[];
21
+ }
22
+
23
+ // Інтерфейс для загального результату синхронізації
24
+ export interface IGeoSyncSummary {
25
+ totalApps: number;
26
+ appsNeedingUpdate: number;
27
+ updates: IGeoUpdateData[];
28
+ errors: string[];
29
+ }
30
+
31
+ /**
32
+ * Отримує всі кампанії з Keitaro
33
+ */
34
+ async function getAllKeitaroCampaigns(): Promise<IKeitaroCampaign[]> {
35
+ try {
36
+ const { data: campaigns } = await keitaroApi.get<IKeitaroCampaign[]>('campaigns');
37
+ return campaigns;
38
+ } catch (error) {
39
+ console.error('Помилка отримання кампаній з Keitaro:', error);
40
+ throw error;
41
+ }
42
+ }
43
+
44
+ /**
45
+ * Отримує всі потоки для конкретної кампанії
46
+ */
47
+ async function getStreamsByCampaignId(campaignId: number): Promise<IKeitaroStream[]> {
48
+ try {
49
+ const { data: streams } = await keitaroApi.get<IKeitaroStream[]>(`campaigns/${campaignId}/streams`);
50
+ return streams;
51
+ } catch (error) {
52
+ console.error(`Помилка отримання потоків для кампанії ${campaignId}:`, error);
53
+ throw error;
54
+ }
55
+ }
56
+
57
+ /**
58
+ * Отримує гео з CLO потоку кампанії
59
+ */
60
+ async function getCLOGeosFromCampaign(campaignId: number): Promise<string[]> {
61
+ try {
62
+ const streams = await getStreamsByCampaignId(campaignId);
63
+ console.log(`Отримано ${streams.length} потоків для кампанії ${campaignId}`);
64
+
65
+ const cloStream = streams.find(stream => stream.name === "CLO");
66
+ if (!cloStream) {
67
+ console.log(`CLO потік не знайдено для кампанії ${campaignId}`);
68
+ return [];
69
+ }
70
+
71
+ console.log(`Знайдено CLO потік:`, {
72
+ id: cloStream.id,
73
+ name: cloStream.name,
74
+ filters: cloStream.filters
75
+ });
76
+
77
+ if (!cloStream.filters || cloStream.filters.length === 0) {
78
+ console.log(`CLO потік не має фільтрів для кампанії ${campaignId}`);
79
+ return [];
80
+ }
81
+
82
+ // Шукаємо фільтр за країною в CLO потоці
83
+ const countryFilter = cloStream.filters.find(filter => filter.name === "country");
84
+ if (!countryFilter) {
85
+ console.log(`Фільтр country не знайдено в CLO потоці кампанії ${campaignId}`);
86
+ return [];
87
+ }
88
+
89
+ console.log(`Знайдено фільтр country:`, countryFilter);
90
+
91
+ if (!countryFilter.payload || !Array.isArray(countryFilter.payload)) {
92
+ console.log(`Фільтр country не має payload або payload не є масивом для кампанії ${campaignId}`);
93
+ return [];
94
+ }
95
+
96
+ console.log(`Гео з CLO потоку кампанії ${campaignId}:`, countryFilter.payload);
97
+ return countryFilter.payload;
98
+
99
+ } catch (error) {
100
+ console.error(`Помилка отримання CLO гео для кампанії ${campaignId}:`, error);
101
+ return [];
102
+ }
103
+ }
104
+
105
+ /**
106
+ * Визначає платформу за назвою кампанії
107
+ */
108
+ function detectPlatformFromCampaignName(campaignName: string): string {
109
+ const name = campaignName.toLowerCase();
110
+
111
+ if (name.includes('(hw)') || name.includes('huawei')) {
112
+ return 'hw';
113
+ } else if (name.includes('(sm)') || name.includes('samsung')) {
114
+ return 'sm';
115
+ } else if (name.includes('(rs)') || name.includes('rustore')) {
116
+ return 'rs';
117
+ } else if (name.includes('(xm)') || name.includes('xiaomi')) {
118
+ return 'xm';
119
+ } else if (name.includes('(ap)') || name.includes('apkpure')) {
120
+ return 'ap';
121
+ } else if (name.includes('(tg)') || name.includes('telegram')) {
122
+ return 'tg';
123
+ } else if (name.includes('(am)') || name.includes('amazon')) {
124
+ return 'am';
125
+ } else {
126
+ return '@'; // General platform
127
+ }
128
+ }
129
+
130
+ /**
131
+ * Знаходить додаток за group кампанії
132
+ */
133
+ function findAppByGroup(group: string, apps: IApp[]): IApp | null {
134
+ const groupId = parseInt(group);
135
+ if (isNaN(groupId)) return null;
136
+ return apps.find(app => app.id === groupId) || null;
137
+ }
138
+
139
+ /**
140
+ * Основна функція синхронізації гео між Keitaro та додатками
141
+ */
142
+ export async function syncKeitaroCLOGeosWithApps(apps: IApp[]): Promise<IGeoSyncSummary> {
143
+ console.log(`Починаємо синхронізацію гео для ${apps.length} додатків...`);
144
+
145
+ const summary: IGeoSyncSummary = {
146
+ totalApps: apps.length,
147
+ appsNeedingUpdate: 0,
148
+ updates: [],
149
+ errors: []
150
+ };
151
+
152
+ try {
153
+ // Отримуємо всі кампанії з Keitaro
154
+ const allCampaigns = await getAllKeitaroCampaigns();
155
+ console.log(`Отримано ${allCampaigns.length} кампаній з Keitaro`);
156
+
157
+ // Діагностика: виводимо всі кампанії з group
158
+ console.log('\n=== Діагностика кампаній ===');
159
+ const campaignsWithGroup = allCampaigns.filter(c => c.group);
160
+ console.log(`Кампанії з group: ${campaignsWithGroup.length}`);
161
+
162
+ // Виводимо перші 10 кампаній з group для діагностики
163
+ campaignsWithGroup.slice(0, 10).forEach(campaign => {
164
+ console.log(` ID: ${campaign.id}, Group: "${campaign.group}", Name: "${campaign.name}"`);
165
+ });
166
+
167
+ if (campaignsWithGroup.length > 10) {
168
+ console.log(` ... та ще ${campaignsWithGroup.length - 10} кампаній`);
169
+ }
170
+
171
+ // Діагностика: шукаємо кампанії з нашими bundle
172
+ console.log('\n=== Пошук кампаній за bundle ===');
173
+ apps.forEach(app => {
174
+ const campaignsWithBundle = allCampaigns.filter(c =>
175
+ c.name.toLowerCase().includes(app.bundle.toLowerCase())
176
+ );
177
+ console.log(`Кампанії з bundle "${app.bundle}": ${campaignsWithBundle.length}`);
178
+ campaignsWithBundle.forEach(campaign => {
179
+ console.log(` ID: ${campaign.id}, Group: "${campaign.group}", Name: "${campaign.name}"`);
180
+ });
181
+ });
182
+
183
+ // Створюємо мапу додатків за ID для швидкого пошуку
184
+ const appsMap = new Map<number, IApp>();
185
+ apps.forEach(app => appsMap.set(app.id, app));
186
+
187
+ console.log(`\nШукаємо додатки з ID: ${Array.from(appsMap.keys()).join(', ')}`);
188
+
189
+ // Фільтруємо кампанії, які мають group відповідний до наших додатків
190
+ const ourCampaigns = allCampaigns.filter(campaign => {
191
+ const hasGroup = campaign.group && appsMap.has(parseInt(campaign.group));
192
+ if (hasGroup) {
193
+ console.log(`✅ Знайдено нашу кампанію за group: ID=${campaign.id}, name="${campaign.name}", group="${campaign.group}"`);
194
+ }
195
+ return hasGroup;
196
+ });
197
+
198
+ // Додатково шукаємо кампанії за bundle в назві
199
+ const campaignsByBundle = allCampaigns.filter(campaign => {
200
+ return apps.some(app =>
201
+ campaign.name.toLowerCase().includes(app.bundle.toLowerCase())
202
+ );
203
+ });
204
+
205
+ console.log(`\nЗнайдено ${campaignsByBundle.length} кампаній за bundle`);
206
+ campaignsByBundle.forEach(campaign => {
207
+ const matchingApp = apps.find(app =>
208
+ campaign.name.toLowerCase().includes(app.bundle.toLowerCase())
209
+ );
210
+ if (matchingApp) {
211
+ console.log(`✅ Знайдено кампанію за bundle: ID=${campaign.id}, name="${campaign.name}", bundle="${matchingApp.bundle}"`);
212
+ }
213
+ });
214
+
215
+ // Об'єднуємо результати
216
+ const allOurCampaigns = [...ourCampaigns, ...campaignsByBundle];
217
+ const uniqueCampaigns = allOurCampaigns.filter((campaign, index, self) =>
218
+ index === self.findIndex(c => c.id === campaign.id)
219
+ );
220
+
221
+ console.log(`\nВсього знайдено ${uniqueCampaigns.length} унікальних кампаній наших додатків`);
222
+
223
+ // Обробляємо кожну кампанію
224
+ for (const campaign of uniqueCampaigns) {
225
+ try {
226
+ const group = campaign.group;
227
+ if (!group) continue;
228
+
229
+ const app = appsMap.get(parseInt(group));
230
+ if (!app) {
231
+ console.warn(`Додаток з ID ${group} не знайдено в списку додатків`);
232
+ continue;
233
+ }
234
+
235
+ console.log(`\nОбробляємо кампанію ${campaign.id} для додатку ${group}:`);
236
+ console.log(` Назва: ${campaign.name}`);
237
+ console.log(` Group: ${campaign.group}`);
238
+
239
+ // Визначаємо платформу за назвою кампанії
240
+ const platform = detectPlatformFromCampaignName(campaign.name);
241
+ console.log(` Визначена платформа: ${platform}`);
242
+
243
+ // Отримуємо гео з CLO потоку кампанії
244
+ console.log(` Отримуємо гео з CLO потоку...`);
245
+ const keitaroGeos = await getCLOGeosFromCampaign(campaign.id);
246
+ console.log(` Keitaro CLO гео: ${keitaroGeos.join(', ')}`);
247
+
248
+ // Отримуємо гео з додатку для конкретної платформи
249
+ const platformData = app.platforms[platform as EPlatform];
250
+ const appGeos = platformData?.geo || [];
251
+ console.log(` App гео для платформи ${platform}: ${appGeos.join(', ')}`);
252
+
253
+ // Порівнюємо гео
254
+ const missingGeos = keitaroGeos.filter(geo => !appGeos.includes(geo));
255
+ const needsUpdate = missingGeos.length > 0;
256
+
257
+ console.log(` Відсутні гео: ${missingGeos.join(', ')}`);
258
+ console.log(` Потребує оновлення: ${needsUpdate}`);
259
+
260
+ if (needsUpdate) {
261
+ summary.appsNeedingUpdate++;
262
+
263
+ const updateData: IGeoUpdateData = {
264
+ appId: parseInt(group),
265
+ platform: platform,
266
+ newGeos: [...appGeos, ...missingGeos]
267
+ };
268
+
269
+ summary.updates.push(updateData);
270
+
271
+ console.log(`✅ Додаток ${group} (${platform}) потребує оновлення гео:`);
272
+ console.log(` Кампанія: ${campaign.name}`);
273
+ console.log(` Keitaro гео: ${keitaroGeos.join(', ')}`);
274
+ console.log(` App гео: ${appGeos.join(', ')}`);
275
+ console.log(` Відсутні гео: ${missingGeos.join(', ')}`);
276
+ console.log(` Нові гео: ${updateData.newGeos.join(', ')}`);
277
+ } else {
278
+ console.log(`✅ Додаток ${group} (${platform}) не потребує оновлення гео`);
279
+ }
280
+
281
+ } catch (error) {
282
+ const errorMsg = `Помилка обробки кампанії ${campaign.id} (${campaign.name}): ${error}`;
283
+ console.error(errorMsg);
284
+ summary.errors.push(errorMsg);
285
+ }
286
+ }
287
+
288
+ console.log(`\nСинхронізація завершена. ${summary.appsNeedingUpdate} додатків потребують оновлення гео.`);
289
+
290
+ } catch (error) {
291
+ const errorMsg = `Критична помилка синхронізації: ${error}`;
292
+ console.error(errorMsg);
293
+ summary.errors.push(errorMsg);
294
+ }
295
+
296
+ return summary;
297
+ }
298
+
299
+ /**
300
+ * Отримує детальну інформацію про синхронізацію для конкретного додатку
301
+ */
302
+ export async function getAppGeoSyncDetails(appId: number, platform: string, apps: IApp[]): Promise<IGeoSyncResult | null> {
303
+ try {
304
+ const app = apps.find(a => a.id === appId);
305
+ if (!app) {
306
+ console.error(`Додаток з ID ${appId} не знайдено`);
307
+ return null;
308
+ }
309
+
310
+ const platformData = app.platforms[platform as EPlatform];
311
+ if (!platformData) {
312
+ console.error(`Платформа ${platform} не знайдена для додатку ${appId}`);
313
+ return null;
314
+ }
315
+
316
+ // Отримуємо всі кампанії та шукаємо нашу за group
317
+ const allCampaigns = await getAllKeitaroCampaigns();
318
+ const ourCampaign = allCampaigns.find(campaign =>
319
+ campaign.group === appId.toString()
320
+ );
321
+
322
+ if (!ourCampaign) {
323
+ console.error(`Кампанія для додатку ${appId} (group: ${appId}) не знайдена в Keitaro`);
324
+ return null;
325
+ }
326
+
327
+ console.log(`Знайдено кампанію для додатку ${appId}:`, {
328
+ id: ourCampaign.id,
329
+ name: ourCampaign.name,
330
+ group: ourCampaign.group
331
+ });
332
+
333
+ // Отримуємо гео з CLO потоку
334
+ const keitaroGeos = await getCLOGeosFromCampaign(ourCampaign.id);
335
+ const appGeos = platformData.geo || [];
336
+
337
+ // Порівнюємо гео
338
+ const missingGeos = keitaroGeos.filter(geo => !appGeos.includes(geo));
339
+ const needsUpdate = missingGeos.length > 0;
340
+
341
+ return {
342
+ appId,
343
+ platform,
344
+ keitaroGeos,
345
+ appGeos,
346
+ missingGeos,
347
+ needsUpdate
348
+ };
349
+
350
+ } catch (error) {
351
+ console.error(`Помилка отримання деталей синхронізації для додатку ${appId}:`, error);
352
+ return null;
353
+ }
354
+ }
355
+
356
+ /**
357
+ * Отримує всі додатки, які потребують оновлення гео
358
+ */
359
+ export async function getAppsNeedingGeoUpdate(apps: IApp[]): Promise<IGeoUpdateData[]> {
360
+ const summary = await syncKeitaroCLOGeosWithApps(apps);
361
+ return summary.updates;
362
+ }
363
+
364
+ /**
365
+ * Експортуємо основні функції
366
+ */
367
+ export const KeitaroCLOGeosService = {
368
+ syncKeitaroCLOGeosWithApps,
369
+ getAppGeoSyncDetails,
370
+ getAppsNeedingGeoUpdate
371
+ };