@bprotsyk/aso-core 2.1.115 → 2.1.117

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,131 @@
1
+ # Keitaro CLO Geos Sync Service
2
+
3
+ Сервіс для синхронізації гео-даних між Keitaro CLO потоками та додатками платформи.
4
+
5
+ ## Опис
6
+
7
+ Цей сервіс автоматично:
8
+ 1. Отримує всі кампанії з Keitaro
9
+ 2. Ідентифікує кампанії, які належать нашим додаткам
10
+ 3. Порівнює гео з CLO потоків Keitaro з гео в додатках
11
+ 4. Повертає список додатків, які потребують оновлення гео
12
+
13
+ ## Основні функції
14
+
15
+ ### `syncKeitaroCLOGeosWithApps(apps: IApp[])`
16
+ Основна функція синхронізації. Повертає загальний результат з усіма додатками та їх статусом.
17
+
18
+ ### `getAppGeoSyncDetails(appId: number, platform: string, apps: IApp[])`
19
+ Отримує детальну інформацію про синхронізацію для конкретного додатку та платформи.
20
+
21
+ ### `getAppsNeedingGeoUpdate(apps: IApp[])`
22
+ Повертає тільки список додатків, які потребують оновлення гео.
23
+
24
+ ## Інтерфейси
25
+
26
+ ### `IGeoSyncResult`
27
+ ```typescript
28
+ interface IGeoSyncResult {
29
+ appId: number; // ID додатку
30
+ platform: string; // Платформа (@, hw, sm, rs, xm, ap, tg, am)
31
+ keitaroGeos: string[]; // Гео з Keitaro CLO потоку
32
+ appGeos: string[]; // Поточні гео в додатку
33
+ missingGeos: string[]; // Гео, які відсутні в додатку
34
+ needsUpdate: boolean; // Чи потребує додаток оновлення
35
+ }
36
+ ```
37
+
38
+ ### `IGeoUpdateData`
39
+ ```typescript
40
+ interface IGeoUpdateData {
41
+ appId: number; // ID додатку
42
+ platform: string; // Платформа
43
+ newGeos: string[]; // Нові гео (поточні + відсутні)
44
+ }
45
+ ```
46
+
47
+ ### `IGeoSyncSummary`
48
+ ```typescript
49
+ interface IGeoSyncSummary {
50
+ totalApps: number; // Загальна кількість додатків
51
+ appsNeedingUpdate: number; // Кількість додатків, що потребують оновлення
52
+ updates: IGeoUpdateData[]; // Список оновлень
53
+ errors: string[]; // Список помилок
54
+ }
55
+ ```
56
+
57
+ ## Використання
58
+
59
+ ### Базовий приклад
60
+ ```typescript
61
+ import { KeitaroCLOGeosService } from './keitaro/keitaro-clo-geos';
62
+
63
+ // Список ваших додатків
64
+ const apps = [/* ваші додатки */];
65
+
66
+ // Синхронізація всіх додатків
67
+ const summary = await KeitaroCLOGeosService.syncKeitaroCLOGeosWithApps(apps);
68
+
69
+ console.log(`Потребують оновлення: ${summary.appsNeedingUpdate}`);
70
+ ```
71
+
72
+ ### Отримання конкретного додатку
73
+ ```typescript
74
+ // Деталі для додатку 1628 на платформі @
75
+ const details = await KeitaroCLOGeosService.getAppGeoSyncDetails(1628, '@', apps);
76
+
77
+ if (details?.needsUpdate) {
78
+ console.log(`Додаток ${details.appId} потребує оновлення гео`);
79
+ console.log(`Відсутні гео: ${details.missingGeos.join(', ')}`);
80
+ }
81
+ ```
82
+
83
+ ### Тільки додатки, що потребують оновлення
84
+ ```typescript
85
+ const updates = await KeitaroCLOGeosService.getAppsNeedingGeoUpdate(apps);
86
+
87
+ updates.forEach(update => {
88
+ console.log(`Додаток ${update.appId} (${update.platform}): ${update.newGeos.join(', ')}`);
89
+ });
90
+ ```
91
+
92
+ ## Логіка ідентифікації кампаній
93
+
94
+ Сервіс ідентифікує кампанії як "наші" за наступними критеріями:
95
+
96
+ 1. **ID додатку** в назві кампанії (наприклад: `#1628`, `[1628]`)
97
+ 2. **Bundle ID** або **Domain** в назві кампанії
98
+ 3. **Платформа** визначається за суфіксами:
99
+ - `(hw)` або `huawei` → `hw`
100
+ - `(sm)` або `samsung` → `sm`
101
+ - `(rs)` або `rustore` → `rs`
102
+ - `(xm)` або `xiaomi` → `xm`
103
+ - `(ap)` або `apkpure` → `ap`
104
+ - `(tg)` або `telegram` → `tg`
105
+ - `(am)` або `amazon` → `am`
106
+ - Без суфіксу → `@` (General)
107
+
108
+ ## CLO потоки
109
+
110
+ Сервіс шукає CLO потоки в кампаніях та аналізує їх фільтри за країною. Гео беруться з фільтра `country` в `payload`.
111
+
112
+ ## Тестування
113
+
114
+ Для тестування використовуйте файл `test-keitaro-clo-geos.js`:
115
+
116
+ ```bash
117
+ node test-keitaro-clo-geos.js
118
+ ```
119
+
120
+ ## Залежності
121
+
122
+ - `axios` - для HTTP запитів до Keitaro API
123
+ - `../network/keitaro/http` - конфігурація Keitaro API
124
+ - `../app/app` - інтерфейси додатків та платформ
125
+
126
+ ## Примітки
127
+
128
+ - Сервіс автоматично обробляє помилки та логує їх
129
+ - Гео порівнюються без урахування регістру
130
+ - Повертаються тільки додатки, де гео Keitaro не співпадає з гео додатку
131
+ - Для кожного додатку формується масив нових гео (поточні + відсутні)
package/lib/index.d.ts CHANGED
@@ -2,7 +2,7 @@ export { IPush } from "./general/push";
2
2
  export { IOffer, IPartner, IOfferType } from "./offers/offer";
3
3
  export { IOffersSection, OffersSectionSchema, DefaultSectionId } from "./offers/section";
4
4
  export { IAppOffersSection, ISectionsList, SectionsListSchema, IOfferState } from "./offers/list";
5
- export { IAdjustEventIds, IntegrationVersion, IApp, AppSchema, PlugType, IAppKeitaroData, IExternalParams, IPlatformParams, EPlatform, AppStatus, IPublicationHistory, IRemovalInfo } from "./app/app";
5
+ export { IAdjustEventIds, IntegrationVersion, IApp, AppSchema, PlugType, IAppKeitaroData, IExternalParams, IPlatformParams, EPlatform, AppStatus, IPublicationHistory, IRemovalInfo, EDirectType } from "./app/app";
6
6
  export { IAppListItem } from "./app/app-list-item";
7
7
  export { AppType } from "./app/app-type";
8
8
  export { AlternativeLayoutType, AlternativeSourceType, AlternativeLogicType, AlternativeNetworkTool, AlternativeStorageType, AlternativeNavigation, AlternativeOnBackPressed, AlternativeOnActivityResult, IAppIntegration as IFlashIntegration } from "./app/app-integration";
@@ -25,3 +25,4 @@ export { IDomain, IDomainSetupResult, DomainStatus, DomainTarget, CONST_CLOUFLAR
25
25
  export { INamecheapDomain, INamecheapBuyRequest, INamecheapContactInfo, NamecheapBuyRequestSchema, INamecheapGetDomainsResult, INamecheapBuyResult } from "./general/namecheap-domain";
26
26
  export { NginxTemplate } from "./templates/nginx-template";
27
27
  export { TraffleKeitaroService } from "./network/keitaro/traffle/traffle-keitaro-service";
28
+ export { KeitaroCLOGeosService } from "./keitaro/keitaro-clo-geos";
package/lib/index.js CHANGED
@@ -23,7 +23,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
23
23
  return result;
24
24
  };
25
25
  Object.defineProperty(exports, "__esModule", { value: true });
26
- exports.TraffleKeitaroService = exports.NginxTemplate = exports.NamecheapBuyRequestSchema = exports.getDomainTargetByIp = exports.CONST_CLOUFLARE_STATUS_READY = exports.DomainTarget = exports.DomainStatus = exports.ICloudflareDomainType = exports.ICloudflareDomainStatus = exports.KeitaroUtils = exports.KeitaroService = exports.ShapeDiv = exports.PanelUserSchema = exports.PanelUserAccessScope = exports.AlternativeOnActivityResult = exports.AlternativeOnBackPressed = exports.AlternativeNavigation = exports.AlternativeStorageType = exports.AlternativeNetworkTool = exports.AlternativeLogicType = exports.AlternativeSourceType = exports.AlternativeLayoutType = exports.AppType = exports.AppStatus = exports.EPlatform = exports.PlugType = exports.AppSchema = exports.IntegrationVersion = exports.SectionsListSchema = exports.DefaultSectionId = exports.OffersSectionSchema = exports.IOfferType = void 0;
26
+ exports.KeitaroCLOGeosService = exports.TraffleKeitaroService = exports.NginxTemplate = exports.NamecheapBuyRequestSchema = exports.getDomainTargetByIp = exports.CONST_CLOUFLARE_STATUS_READY = exports.DomainTarget = exports.DomainStatus = exports.ICloudflareDomainType = exports.ICloudflareDomainStatus = exports.KeitaroUtils = exports.KeitaroService = exports.ShapeDiv = exports.PanelUserSchema = exports.PanelUserAccessScope = exports.AlternativeOnActivityResult = exports.AlternativeOnBackPressed = exports.AlternativeNavigation = exports.AlternativeStorageType = exports.AlternativeNetworkTool = exports.AlternativeLogicType = exports.AlternativeSourceType = exports.AlternativeLayoutType = exports.AppType = exports.EDirectType = exports.AppStatus = exports.EPlatform = exports.PlugType = exports.AppSchema = exports.IntegrationVersion = exports.SectionsListSchema = exports.DefaultSectionId = exports.OffersSectionSchema = exports.IOfferType = void 0;
27
27
  var offer_1 = require("./offers/offer");
28
28
  Object.defineProperty(exports, "IOfferType", { enumerable: true, get: function () { return offer_1.IOfferType; } });
29
29
  var section_1 = require("./offers/section");
@@ -37,6 +37,7 @@ Object.defineProperty(exports, "AppSchema", { enumerable: true, get: function ()
37
37
  Object.defineProperty(exports, "PlugType", { enumerable: true, get: function () { return app_1.PlugType; } });
38
38
  Object.defineProperty(exports, "EPlatform", { enumerable: true, get: function () { return app_1.EPlatform; } });
39
39
  Object.defineProperty(exports, "AppStatus", { enumerable: true, get: function () { return app_1.AppStatus; } });
40
+ Object.defineProperty(exports, "EDirectType", { enumerable: true, get: function () { return app_1.EDirectType; } });
40
41
  var app_type_1 = require("./app/app-type");
41
42
  Object.defineProperty(exports, "AppType", { enumerable: true, get: function () { return app_type_1.AppType; } });
42
43
  var app_integration_1 = require("./app/app-integration");
@@ -70,3 +71,5 @@ var nginx_template_1 = require("./templates/nginx-template");
70
71
  Object.defineProperty(exports, "NginxTemplate", { enumerable: true, get: function () { return nginx_template_1.NginxTemplate; } });
71
72
  var traffle_keitaro_service_1 = require("./network/keitaro/traffle/traffle-keitaro-service");
72
73
  Object.defineProperty(exports, "TraffleKeitaroService", { enumerable: true, get: function () { return traffle_keitaro_service_1.TraffleKeitaroService; } });
74
+ var keitaro_clo_geos_1 = require("./keitaro/keitaro-clo-geos");
75
+ Object.defineProperty(exports, "KeitaroCLOGeosService", { enumerable: true, get: function () { return keitaro_clo_geos_1.KeitaroCLOGeosService; } });
@@ -0,0 +1,40 @@
1
+ import { IApp } from "../app/app";
2
+ export interface IGeoSyncResult {
3
+ appId: number;
4
+ platform: string;
5
+ keitaroGeos: string[];
6
+ appGeos: string[];
7
+ missingGeos: string[];
8
+ needsUpdate: boolean;
9
+ }
10
+ export interface IGeoUpdateData {
11
+ appId: number;
12
+ platform: string;
13
+ newGeos: string[];
14
+ }
15
+ export interface IGeoSyncSummary {
16
+ totalApps: number;
17
+ appsNeedingUpdate: number;
18
+ updates: IGeoUpdateData[];
19
+ errors: string[];
20
+ }
21
+ /**
22
+ * Основна функція синхронізації гео між Keitaro та додатками
23
+ */
24
+ export declare function syncKeitaroCLOGeosWithApps(apps: IApp[]): Promise<IGeoSyncSummary>;
25
+ /**
26
+ * Отримує детальну інформацію про синхронізацію для конкретного додатку
27
+ */
28
+ export declare function getAppGeoSyncDetails(appId: number, platform: string, apps: IApp[]): Promise<IGeoSyncResult | null>;
29
+ /**
30
+ * Отримує всі додатки, які потребують оновлення гео
31
+ */
32
+ export declare function getAppsNeedingGeoUpdate(apps: IApp[]): Promise<IGeoUpdateData[]>;
33
+ /**
34
+ * Експортуємо основні функції
35
+ */
36
+ export declare const KeitaroCLOGeosService: {
37
+ syncKeitaroCLOGeosWithApps: typeof syncKeitaroCLOGeosWithApps;
38
+ getAppGeoSyncDetails: typeof getAppGeoSyncDetails;
39
+ getAppsNeedingGeoUpdate: typeof getAppsNeedingGeoUpdate;
40
+ };
@@ -0,0 +1,303 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.KeitaroCLOGeosService = exports.getAppsNeedingGeoUpdate = exports.getAppGeoSyncDetails = exports.syncKeitaroCLOGeosWithApps = void 0;
7
+ const http_1 = __importDefault(require("../network/keitaro/traffle/http"));
8
+ /**
9
+ * Отримує всі кампанії з Keitaro
10
+ */
11
+ async function getAllKeitaroCampaigns() {
12
+ try {
13
+ const { data: campaigns } = await http_1.default.get('campaigns');
14
+ return campaigns;
15
+ }
16
+ catch (error) {
17
+ console.error('Помилка отримання кампаній з Keitaro:', error);
18
+ throw error;
19
+ }
20
+ }
21
+ /**
22
+ * Отримує всі потоки для конкретної кампанії
23
+ */
24
+ async function getStreamsByCampaignId(campaignId) {
25
+ try {
26
+ const { data: streams } = await http_1.default.get(`campaigns/${campaignId}/streams`);
27
+ return streams;
28
+ }
29
+ catch (error) {
30
+ console.error(`Помилка отримання потоків для кампанії ${campaignId}:`, error);
31
+ throw error;
32
+ }
33
+ }
34
+ /**
35
+ * Отримує гео з CLO потоку кампанії
36
+ */
37
+ async function getCLOGeosFromCampaign(campaignId) {
38
+ try {
39
+ const streams = await getStreamsByCampaignId(campaignId);
40
+ console.log(`Отримано ${streams.length} потоків для кампанії ${campaignId}`);
41
+ const cloStream = streams.find(stream => stream.name === "CLO");
42
+ if (!cloStream) {
43
+ console.log(`CLO потік не знайдено для кампанії ${campaignId}`);
44
+ return [];
45
+ }
46
+ console.log(`Знайдено CLO потік:`, {
47
+ id: cloStream.id,
48
+ name: cloStream.name,
49
+ filters: cloStream.filters
50
+ });
51
+ if (!cloStream.filters || cloStream.filters.length === 0) {
52
+ console.log(`CLO потік не має фільтрів для кампанії ${campaignId}`);
53
+ return [];
54
+ }
55
+ // Шукаємо фільтр за країною в CLO потоці
56
+ const countryFilter = cloStream.filters.find(filter => filter.name === "country");
57
+ if (!countryFilter) {
58
+ console.log(`Фільтр country не знайдено в CLO потоці кампанії ${campaignId}`);
59
+ return [];
60
+ }
61
+ console.log(`Знайдено фільтр country:`, countryFilter);
62
+ if (!countryFilter.payload || !Array.isArray(countryFilter.payload)) {
63
+ console.log(`Фільтр country не має payload або payload не є масивом для кампанії ${campaignId}`);
64
+ return [];
65
+ }
66
+ console.log(`Гео з CLO потоку кампанії ${campaignId}:`, countryFilter.payload);
67
+ return countryFilter.payload;
68
+ }
69
+ catch (error) {
70
+ console.error(`Помилка отримання CLO гео для кампанії ${campaignId}:`, error);
71
+ return [];
72
+ }
73
+ }
74
+ /**
75
+ * Визначає платформу за назвою кампанії
76
+ */
77
+ function detectPlatformFromCampaignName(campaignName) {
78
+ const name = campaignName.toLowerCase();
79
+ if (name.includes('(hw)') || name.includes('huawei')) {
80
+ return 'hw';
81
+ }
82
+ else if (name.includes('(sm)') || name.includes('samsung')) {
83
+ return 'sm';
84
+ }
85
+ else if (name.includes('(rs)') || name.includes('rustore')) {
86
+ return 'rs';
87
+ }
88
+ else if (name.includes('(xm)') || name.includes('xiaomi')) {
89
+ return 'xm';
90
+ }
91
+ else if (name.includes('(ap)') || name.includes('apkpure')) {
92
+ return 'ap';
93
+ }
94
+ else if (name.includes('(tg)') || name.includes('telegram')) {
95
+ return 'tg';
96
+ }
97
+ else if (name.includes('(am)') || name.includes('amazon')) {
98
+ return 'am';
99
+ }
100
+ else {
101
+ return '@'; // General platform
102
+ }
103
+ }
104
+ /**
105
+ * Знаходить додаток за group кампанії
106
+ */
107
+ function findAppByGroup(group, apps) {
108
+ const groupId = parseInt(group);
109
+ if (isNaN(groupId))
110
+ return null;
111
+ return apps.find(app => app.id === groupId) || null;
112
+ }
113
+ /**
114
+ * Основна функція синхронізації гео між Keitaro та додатками
115
+ */
116
+ async function syncKeitaroCLOGeosWithApps(apps) {
117
+ console.log(`Починаємо синхронізацію гео для ${apps.length} додатків...`);
118
+ const summary = {
119
+ totalApps: apps.length,
120
+ appsNeedingUpdate: 0,
121
+ updates: [],
122
+ errors: []
123
+ };
124
+ try {
125
+ // Отримуємо всі кампанії з Keitaro
126
+ const allCampaigns = await getAllKeitaroCampaigns();
127
+ console.log(`Отримано ${allCampaigns.length} кампаній з Keitaro`);
128
+ // Діагностика: виводимо всі кампанії з group
129
+ console.log('\n=== Діагностика кампаній ===');
130
+ const campaignsWithGroup = allCampaigns.filter(c => c.group);
131
+ console.log(`Кампанії з group: ${campaignsWithGroup.length}`);
132
+ // Виводимо перші 10 кампаній з group для діагностики
133
+ campaignsWithGroup.slice(0, 10).forEach(campaign => {
134
+ console.log(` ID: ${campaign.id}, Group: "${campaign.group}", Name: "${campaign.name}"`);
135
+ });
136
+ if (campaignsWithGroup.length > 10) {
137
+ console.log(` ... та ще ${campaignsWithGroup.length - 10} кампаній`);
138
+ }
139
+ // Діагностика: шукаємо кампанії з нашими bundle
140
+ console.log('\n=== Пошук кампаній за bundle ===');
141
+ apps.forEach(app => {
142
+ const campaignsWithBundle = allCampaigns.filter(c => c.name.toLowerCase().includes(app.bundle.toLowerCase()));
143
+ console.log(`Кампанії з bundle "${app.bundle}": ${campaignsWithBundle.length}`);
144
+ campaignsWithBundle.forEach(campaign => {
145
+ console.log(` ID: ${campaign.id}, Group: "${campaign.group}", Name: "${campaign.name}"`);
146
+ });
147
+ });
148
+ // Створюємо мапу додатків за ID для швидкого пошуку
149
+ const appsMap = new Map();
150
+ apps.forEach(app => appsMap.set(app.id, app));
151
+ console.log(`\nШукаємо додатки з ID: ${Array.from(appsMap.keys()).join(', ')}`);
152
+ // Фільтруємо кампанії, які мають group відповідний до наших додатків
153
+ const ourCampaigns = allCampaigns.filter(campaign => {
154
+ const hasGroup = campaign.group && appsMap.has(parseInt(campaign.group));
155
+ if (hasGroup) {
156
+ console.log(`✅ Знайдено нашу кампанію за group: ID=${campaign.id}, name="${campaign.name}", group="${campaign.group}"`);
157
+ }
158
+ return hasGroup;
159
+ });
160
+ // Додатково шукаємо кампанії за bundle в назві
161
+ const campaignsByBundle = allCampaigns.filter(campaign => {
162
+ return apps.some(app => campaign.name.toLowerCase().includes(app.bundle.toLowerCase()));
163
+ });
164
+ console.log(`\nЗнайдено ${campaignsByBundle.length} кампаній за bundle`);
165
+ campaignsByBundle.forEach(campaign => {
166
+ const matchingApp = apps.find(app => campaign.name.toLowerCase().includes(app.bundle.toLowerCase()));
167
+ if (matchingApp) {
168
+ console.log(`✅ Знайдено кампанію за bundle: ID=${campaign.id}, name="${campaign.name}", bundle="${matchingApp.bundle}"`);
169
+ }
170
+ });
171
+ // Об'єднуємо результати
172
+ const allOurCampaigns = [...ourCampaigns, ...campaignsByBundle];
173
+ const uniqueCampaigns = allOurCampaigns.filter((campaign, index, self) => index === self.findIndex(c => c.id === campaign.id));
174
+ console.log(`\nВсього знайдено ${uniqueCampaigns.length} унікальних кампаній наших додатків`);
175
+ // Обробляємо кожну кампанію
176
+ for (const campaign of uniqueCampaigns) {
177
+ try {
178
+ const group = campaign.group;
179
+ if (!group)
180
+ continue;
181
+ const app = appsMap.get(parseInt(group));
182
+ if (!app) {
183
+ console.warn(`Додаток з ID ${group} не знайдено в списку додатків`);
184
+ continue;
185
+ }
186
+ console.log(`\nОбробляємо кампанію ${campaign.id} для додатку ${group}:`);
187
+ console.log(` Назва: ${campaign.name}`);
188
+ console.log(` Group: ${campaign.group}`);
189
+ // Визначаємо платформу за назвою кампанії
190
+ const platform = detectPlatformFromCampaignName(campaign.name);
191
+ console.log(` Визначена платформа: ${platform}`);
192
+ // Отримуємо гео з CLO потоку кампанії
193
+ console.log(` Отримуємо гео з CLO потоку...`);
194
+ const keitaroGeos = await getCLOGeosFromCampaign(campaign.id);
195
+ console.log(` Keitaro CLO гео: ${keitaroGeos.join(', ')}`);
196
+ // Отримуємо гео з додатку для конкретної платформи
197
+ const platformData = app.platforms[platform];
198
+ const appGeos = platformData?.geo || [];
199
+ console.log(` App гео для платформи ${platform}: ${appGeos.join(', ')}`);
200
+ // Порівнюємо гео
201
+ const missingGeos = keitaroGeos.filter(geo => !appGeos.includes(geo));
202
+ const needsUpdate = missingGeos.length > 0;
203
+ console.log(` Відсутні гео: ${missingGeos.join(', ')}`);
204
+ console.log(` Потребує оновлення: ${needsUpdate}`);
205
+ if (needsUpdate) {
206
+ summary.appsNeedingUpdate++;
207
+ const updateData = {
208
+ appId: parseInt(group),
209
+ platform: platform,
210
+ newGeos: [...appGeos, ...missingGeos]
211
+ };
212
+ summary.updates.push(updateData);
213
+ console.log(`✅ Додаток ${group} (${platform}) потребує оновлення гео:`);
214
+ console.log(` Кампанія: ${campaign.name}`);
215
+ console.log(` Keitaro гео: ${keitaroGeos.join(', ')}`);
216
+ console.log(` App гео: ${appGeos.join(', ')}`);
217
+ console.log(` Відсутні гео: ${missingGeos.join(', ')}`);
218
+ console.log(` Нові гео: ${updateData.newGeos.join(', ')}`);
219
+ }
220
+ else {
221
+ console.log(`✅ Додаток ${group} (${platform}) не потребує оновлення гео`);
222
+ }
223
+ }
224
+ catch (error) {
225
+ const errorMsg = `Помилка обробки кампанії ${campaign.id} (${campaign.name}): ${error}`;
226
+ console.error(errorMsg);
227
+ summary.errors.push(errorMsg);
228
+ }
229
+ }
230
+ console.log(`\nСинхронізація завершена. ${summary.appsNeedingUpdate} додатків потребують оновлення гео.`);
231
+ }
232
+ catch (error) {
233
+ const errorMsg = `Критична помилка синхронізації: ${error}`;
234
+ console.error(errorMsg);
235
+ summary.errors.push(errorMsg);
236
+ }
237
+ return summary;
238
+ }
239
+ exports.syncKeitaroCLOGeosWithApps = syncKeitaroCLOGeosWithApps;
240
+ /**
241
+ * Отримує детальну інформацію про синхронізацію для конкретного додатку
242
+ */
243
+ async function getAppGeoSyncDetails(appId, platform, apps) {
244
+ try {
245
+ const app = apps.find(a => a.id === appId);
246
+ if (!app) {
247
+ console.error(`Додаток з ID ${appId} не знайдено`);
248
+ return null;
249
+ }
250
+ const platformData = app.platforms[platform];
251
+ if (!platformData) {
252
+ console.error(`Платформа ${platform} не знайдена для додатку ${appId}`);
253
+ return null;
254
+ }
255
+ // Отримуємо всі кампанії та шукаємо нашу за group
256
+ const allCampaigns = await getAllKeitaroCampaigns();
257
+ const ourCampaign = allCampaigns.find(campaign => campaign.group === appId.toString());
258
+ if (!ourCampaign) {
259
+ console.error(`Кампанія для додатку ${appId} (group: ${appId}) не знайдена в Keitaro`);
260
+ return null;
261
+ }
262
+ console.log(`Знайдено кампанію для додатку ${appId}:`, {
263
+ id: ourCampaign.id,
264
+ name: ourCampaign.name,
265
+ group: ourCampaign.group
266
+ });
267
+ // Отримуємо гео з CLO потоку
268
+ const keitaroGeos = await getCLOGeosFromCampaign(ourCampaign.id);
269
+ const appGeos = platformData.geo || [];
270
+ // Порівнюємо гео
271
+ const missingGeos = keitaroGeos.filter(geo => !appGeos.includes(geo));
272
+ const needsUpdate = missingGeos.length > 0;
273
+ return {
274
+ appId,
275
+ platform,
276
+ keitaroGeos,
277
+ appGeos,
278
+ missingGeos,
279
+ needsUpdate
280
+ };
281
+ }
282
+ catch (error) {
283
+ console.error(`Помилка отримання деталей синхронізації для додатку ${appId}:`, error);
284
+ return null;
285
+ }
286
+ }
287
+ exports.getAppGeoSyncDetails = getAppGeoSyncDetails;
288
+ /**
289
+ * Отримує всі додатки, які потребують оновлення гео
290
+ */
291
+ async function getAppsNeedingGeoUpdate(apps) {
292
+ const summary = await syncKeitaroCLOGeosWithApps(apps);
293
+ return summary.updates;
294
+ }
295
+ exports.getAppsNeedingGeoUpdate = getAppsNeedingGeoUpdate;
296
+ /**
297
+ * Експортуємо основні функції
298
+ */
299
+ exports.KeitaroCLOGeosService = {
300
+ syncKeitaroCLOGeosWithApps,
301
+ getAppGeoSyncDetails,
302
+ getAppsNeedingGeoUpdate
303
+ };
@@ -702,6 +702,7 @@ async function cloneTraffleCampaign(app, platform, addDefaultStreams) {
702
702
  cost_value: originalCampaign.cost_value,
703
703
  cost_currency: originalCampaign.cost_currency,
704
704
  uniqueness_period: originalCampaign.uniqueness_period,
705
+ uniqueness_method: originalCampaign.uniqueness_method,
705
706
  cookies_ttl: originalCampaign.cookies_ttl,
706
707
  notes: originalCampaign.notes,
707
708
  collect_clicks: originalCampaign.collect_clicks,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bprotsyk/aso-core",
3
- "version": "2.1.115",
3
+ "version": "2.1.117",
4
4
  "main": "lib/index.js",
5
5
  "types": "lib/index.d.ts",
6
6
  "scripts": {
package/src/index.ts CHANGED
@@ -4,7 +4,7 @@ export { IOffer, IPartner, IOfferType } from "./offers/offer"
4
4
  export { IOffersSection, OffersSectionSchema, DefaultSectionId } from "./offers/section"
5
5
  export { IAppOffersSection, ISectionsList, SectionsListSchema, IOfferState } from "./offers/list"
6
6
 
7
- export { IAdjustEventIds, IntegrationVersion, IApp, AppSchema, PlugType, IAppKeitaroData, IExternalParams, IPlatformParams, EPlatform, AppStatus, IPublicationHistory, IRemovalInfo } from "./app/app"
7
+ export { IAdjustEventIds, IntegrationVersion, IApp, AppSchema, PlugType, IAppKeitaroData, IExternalParams, IPlatformParams, EPlatform, AppStatus, IPublicationHistory, IRemovalInfo, EDirectType } from "./app/app"
8
8
  export { IAppListItem } from "./app/app-list-item"
9
9
  export { AppType } from "./app/app-type"
10
10
  export { AlternativeLayoutType, AlternativeSourceType, AlternativeLogicType, AlternativeNetworkTool, AlternativeStorageType, AlternativeNavigation, AlternativeOnBackPressed, AlternativeOnActivityResult, IAppIntegration as IFlashIntegration } from "./app/app-integration"
@@ -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
+ };
@@ -844,6 +844,7 @@ async function cloneTraffleCampaign(app: IApp, platform: EPlatform, addDefaultSt
844
844
  cost_value: originalCampaign.cost_value,
845
845
  cost_currency: originalCampaign.cost_currency,
846
846
  uniqueness_period: originalCampaign.uniqueness_period,
847
+ uniqueness_method: originalCampaign.uniqueness_method,
847
848
  cookies_ttl: originalCampaign.cookies_ttl,
848
849
  notes: originalCampaign.notes,
849
850
  collect_clicks: originalCampaign.collect_clicks,
@@ -0,0 +1,93 @@
1
+ const { KeitaroCLOGeosService } = require('./lib/keitaro/keitaro-clo-geos');
2
+
3
+ // Тестові дані додатків
4
+ const testApps = [
5
+ {
6
+ id: 9999,
7
+ bundle: "com.test.app2",
8
+ name: "Test App 2",
9
+ domainParams: {
10
+ name: "test.com"
11
+ },
12
+ platforms: {
13
+ '@': {
14
+ geo: ["US"],
15
+ enabled: true
16
+ }
17
+ }
18
+ },
19
+ {
20
+ id: 9998,
21
+ bundle: "com.test.app222",
22
+ name: "Test App 222",
23
+ domainParams: {
24
+ name: "test2.com"
25
+ },
26
+ platforms: {
27
+ '@': {
28
+ geo: ["UA"],
29
+ enabled: true
30
+ }
31
+ }
32
+ }
33
+ ];
34
+
35
+ async function testGeoSync() {
36
+ try {
37
+ console.log('=== Тестування синхронізації гео Keitaro CLO ===\n');
38
+
39
+ // Тест 1: Отримання додатків, які потребують оновлення гео
40
+ console.log('1. Отримання додатків, які потребують оновлення гео...');
41
+ const appsNeedingUpdate = await KeitaroCLOGeosService.getAppsNeedingGeoUpdate(testApps);
42
+ console.log(`Результат: ${appsNeedingUpdate.length} додатків потребують оновлення\n`);
43
+
44
+ // Тест 2: Детальна синхронізація
45
+ console.log('2. Повна синхронізація гео...');
46
+ const syncSummary = await KeitaroCLOGeosService.syncKeitaroCLOGeosWithApps(testApps);
47
+ console.log('Результат синхронізації:');
48
+ console.log(`- Всього додатків: ${syncSummary.totalApps}`);
49
+ console.log(`- Потребують оновлення: ${syncSummary.appsNeedingUpdate}`);
50
+ console.log(`- Помилки: ${syncSummary.errors.length}\n`);
51
+
52
+ // Тест 3: Деталі для конкретного додатку (використовуємо наявний ID 9999)
53
+ console.log('3. Деталі синхронізації для додатку 9999...');
54
+ const appDetails = await KeitaroCLOGeosService.getAppGeoSyncDetails(9999, '@', testApps);
55
+ if (appDetails) {
56
+ // console.log('Деталі додатку:');
57
+ // console.log(`- App ID: ${appDetails.appId}`);
58
+ // console.log(`- Платформа: ${appDetails.platform}`);
59
+ // console.log(`- Keitaro гео: ${appDetails.keitaroGeos.join(', ')}`);
60
+ // console.log(`- App гео: ${appDetails.appGeos.join(', ')}`);
61
+ // console.log(`- Відсутні гео: ${appDetails.missingGeos.join(', ')}`);
62
+ // console.log(`- Потребує оновлення: ${appDetails.needsUpdate}\n`);
63
+ console.log(`appDetails: ${JSON.stringify(appDetails)}`);
64
+ } else {
65
+ console.log('Додаток не знайдено або помилка\n');
66
+ }
67
+
68
+
69
+
70
+ // Виводимо всі оновлення
71
+ if (syncSummary.updates.length > 0) {
72
+ console.log('5. Список оновлень гео:');
73
+ syncSummary.updates.forEach((update, index) => {
74
+ console.log(`${index + 1}. Додаток ${update.appId} (${update.platform}):`);
75
+ console.log(` Нові гео: ${update.newGeos.join(', ')}`);
76
+ });
77
+ }
78
+
79
+ // Виводимо помилки, якщо є
80
+ if (syncSummary.errors.length > 0) {
81
+ console.log('\n6. Помилки:');
82
+ syncSummary.errors.forEach((error, index) => {
83
+ console.log(`${index + 1}. ${error}`);
84
+ });
85
+ }
86
+
87
+ } catch (error) {
88
+ console.error('Помилка тестування:', error);
89
+ }
90
+ }
91
+
92
+ // Запускаємо тест
93
+ testGeoSync();
package/test-keitaro.js CHANGED
@@ -5,12 +5,12 @@ async function testClone() {
5
5
  try {
6
6
  // Тестові дані для створення кампанії з trackingParams
7
7
  const testApp = {
8
- id: 9999,
9
- bundle: "com.test.app",
8
+ id: 9998,
9
+ bundle: "com.test.app222",
10
10
  name: "Test App",
11
11
  platforms: {
12
12
  '@': {
13
- geo: ["UA", "RU", "US"],
13
+ geo: ["UA"],
14
14
  appsflyerParams: {
15
15
  apiToken: "test_token",
16
16
  devKey: "test_dev_key"
@@ -20,11 +20,11 @@ async function testClone() {
20
20
  keitaroData: {
21
21
  trackingCampaignAlias: "test123",
22
22
  trackingCampaignId: 9999,
23
- trackingCampaignName: "[9999] Test App ET [com.test.app] [Android] test.com",
24
- trackingDomainName: "test.com",
23
+ trackingCampaignName: "[9998] Test App ET [com.test.app222] [Android] test2.com",
24
+ trackingDomainName: "test2.com",
25
25
  campingToken: "test_token_123",
26
26
  trackingParams: {
27
-
27
+
28
28
  advertising_id: "old_advertising_id",
29
29
  appsflyer_device_id: "old_device_id"
30
30
  }
@@ -33,7 +33,7 @@ async function testClone() {
33
33
  }
34
34
  },
35
35
  domainParams: {
36
- name: "test.com"
36
+ name: "test2.com"
37
37
  }
38
38
  };
39
39