@bprotsyk/aso-core 2.1.99 → 2.1.101
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 +11 -2
- package/lib/app/app.js +2 -0
- package/lib/general/domain.d.ts +1 -0
- package/lib/general/namecheap-domain.d.ts +1 -0
- package/lib/index.d.ts +1 -1
- package/lib/keitaro/keitaro-campaign.d.ts +1 -0
- package/lib/network/keitaro/traffle/http.js +2 -1
- package/lib/network/keitaro/traffle/traffle-keitaro-service.d.ts +4 -0
- package/lib/network/keitaro/traffle/traffle-keitaro-service.js +255 -1
- package/lib/offers/list.d.ts +1 -0
- package/lib/offers/offer.d.ts +1 -0
- package/lib/offers/section.d.ts +1 -0
- package/lib/panel/user.d.ts +1 -0
- package/package.json +1 -1
- package/src/app/app.ts +8 -3
- package/src/index.ts +1 -1
- package/src/keitaro/keitaro-campaign.ts +1 -0
- package/src/network/keitaro/keitaro-service.ts +6 -0
- package/src/network/keitaro/traffle/http.ts +2 -1
- package/src/network/keitaro/traffle/traffle-keitaro-service.ts +304 -1
- package/test-keitaro.js +27 -2
- package/tsc +0 -0
- package/lib/keitaro/keitaro-conversions.d.ts +0 -52
- package/lib/keitaro/keitaro-conversions.js +0 -27
- package/lib/keitaro/keitaro-utils.d.ts +0 -2
- package/lib/keitaro/keitaro-utils.js +0 -108
- package/lib/keitaro-utils.d.ts +0 -2
- package/lib/keitaro-utils.js +0 -136
- package/lib/utils/comparing.d.ts +0 -31
- package/lib/utils/comparing.js +0 -104
|
@@ -10,6 +10,7 @@ import { IKeitaroOffer, IKeitaroOffersFilter } from "index";
|
|
|
10
10
|
import { IKeitaroClicksRequest, IKeitaroClicksRangeRequest, KeitaroClicksInterval } from "../../../keitaro/keitaro-clicks";
|
|
11
11
|
import { OpenAI } from "../../../network/openai/openai";
|
|
12
12
|
import { off } from "process";
|
|
13
|
+
import { IKeitaroCampaignParameters } from "../../../keitaro/keitaro-campaign";
|
|
13
14
|
|
|
14
15
|
async function getAllOffers(): Promise<IKeitaroOffer[]> {
|
|
15
16
|
const { data: offers } = await keitaroApi.get<IKeitaroOffer[]>('offers')
|
|
@@ -43,6 +44,12 @@ export interface IAffiliateNetwork {
|
|
|
43
44
|
offers: number;
|
|
44
45
|
}
|
|
45
46
|
|
|
47
|
+
interface IParamsGenerationsOpenAIResponse {
|
|
48
|
+
naming: string;
|
|
49
|
+
advertisingid: string;
|
|
50
|
+
appsflyerDeviceID: string;
|
|
51
|
+
}
|
|
52
|
+
|
|
46
53
|
async function getTraffleOffersGroups(): Promise<any[]> {
|
|
47
54
|
try {
|
|
48
55
|
const response = await keitaroApi.get('/groups?type=offers');
|
|
@@ -147,6 +154,302 @@ async function updateOfferLinkById(id: number, action_payload: string | object):
|
|
|
147
154
|
}
|
|
148
155
|
|
|
149
156
|
|
|
157
|
+
async function getCampaignById(id: number): Promise<IKeitaroCampaign> {
|
|
158
|
+
const { data: campaign } = await keitaroApi.get(`/campaigns/${id}`);
|
|
159
|
+
return campaign
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
async function getAllCampaigns(): Promise<IKeitaroCampaign[]> {
|
|
163
|
+
const { data: campaigns } = await keitaroApi.get<IKeitaroCampaign[]>('campaigns');
|
|
164
|
+
return campaigns
|
|
165
|
+
}
|
|
166
|
+
async function getDomains(onlyActive?: boolean): Promise<IKeitaroDomain[]> {
|
|
167
|
+
const { data: domains } = await keitaroApi.get<IKeitaroDomain[]>(`/domains`)
|
|
168
|
+
return onlyActive ? domains.filter((d) => d.network_status == "active") : domains
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
async function getStreamsByCampaignId(campaignId: number): Promise<IKeitaroStream[]> {
|
|
172
|
+
const { data: streams } = await keitaroApi.get<IKeitaroStream[]>(`campaigns/${campaignId}/streams`);
|
|
173
|
+
|
|
174
|
+
return streams
|
|
175
|
+
}
|
|
176
|
+
async function createCampaign(campaignData: Partial<IKeitaroCampaign>): Promise<IKeitaroCampaign> {
|
|
177
|
+
let { data: campaign } = await keitaroApi.post(`/campaigns`, campaignData)
|
|
178
|
+
|
|
179
|
+
return campaign
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
async function TrafleKeitaroParameters(parameters: any) {
|
|
184
|
+
const {bundle, appsflyerDevKey, name} = parameters
|
|
185
|
+
|
|
186
|
+
let openAIResponse = await OpenAI.requestOpenAI({
|
|
187
|
+
model: "o3-mini",
|
|
188
|
+
messages: [
|
|
189
|
+
{ role: "user", content: `sid for ${bundle} ${name} generations Generate **one** random English word in full lowercase (still lowercase, no numbers or special characters) for the parameters "naming", "advertisingid", "appsflyerDeviceID". Return **ONLY** an JSON object: { "naming": "word", "advertisingid": "word", "appsflyerDeviceID": "word"}`},
|
|
190
|
+
]
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
if (!openAIResponse) {
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
let ParamsGenerationsOpenAIResponse: IParamsGenerationsOpenAIResponse | null = null;
|
|
198
|
+
try {
|
|
199
|
+
ParamsGenerationsOpenAIResponse = JSON.parse(openAIResponse);
|
|
200
|
+
} catch (error) {
|
|
201
|
+
console.error("Failed to parse OpenAI response:", error);
|
|
202
|
+
return null;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
console.log(`ParamsGenerationsOpenAIResponse: ${JSON.stringify(ParamsGenerationsOpenAIResponse)}`)
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
return {
|
|
209
|
+
parameters: {
|
|
210
|
+
keyword: { name: 'keyword', placeholder: '', alias: '' },
|
|
211
|
+
cost: { name: 'cost', placeholder: '', alias: '' },
|
|
212
|
+
currency: { name: 'currency', placeholder: '', alias: '' },
|
|
213
|
+
external_id: { name: 'external_id', placeholder: '', alias: '' },
|
|
214
|
+
creative_id: { name: 'creative_id', placeholder: '', alias: '' },
|
|
215
|
+
ad_campaign_id: { name: 'ad_campaign_id', placeholder: '', alias: '' },
|
|
216
|
+
source: { name: 'source', placeholder: '', alias: '' },
|
|
217
|
+
sub_id_1: { name: 'uqID', placeholder: '', alias: '' },
|
|
218
|
+
sub_id_2: { name: 'AD_ID', placeholder: '', alias: '' },
|
|
219
|
+
sub_id_3: { name: 'USB', placeholder: '', alias: '' },
|
|
220
|
+
sub_id_4: { name: 'DEV', placeholder: '', alias: '' },
|
|
221
|
+
sub_id_5: { name: 'LANG', placeholder: '', alias: '' },
|
|
222
|
+
sub_id_10: { name: 'market', placeholder: '', alias: '' },
|
|
223
|
+
sub_id_15: { name: ParamsGenerationsOpenAIResponse?.naming, placeholder: '{naming}', alias: 'Naming' },
|
|
224
|
+
sub_id_16: {
|
|
225
|
+
name: 'bundle_id',
|
|
226
|
+
placeholder: bundle,
|
|
227
|
+
alias: 'Bundle ID'
|
|
228
|
+
},
|
|
229
|
+
sub_id_17: {
|
|
230
|
+
name: ParamsGenerationsOpenAIResponse?.advertisingid,
|
|
231
|
+
placeholder: '{advertising_id}',
|
|
232
|
+
alias: 'Advertising ID'
|
|
233
|
+
},
|
|
234
|
+
sub_id_18: {
|
|
235
|
+
name: ParamsGenerationsOpenAIResponse?.appsflyerDeviceID,
|
|
236
|
+
placeholder: '{appsflyer_device_id}',
|
|
237
|
+
alias: 'Appsflyer Device ID'
|
|
238
|
+
},
|
|
239
|
+
sub_id_19: {
|
|
240
|
+
name: 'appsflyer_dev_key',
|
|
241
|
+
placeholder: appsflyerDevKey,
|
|
242
|
+
alias: 'Appsflyer Dev Key'
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
async function cloneTraffleCampaign(app: IApp, platform: EPlatform, addDefaultStreams?: boolean): Promise<IKeitaroCampaign | any> {
|
|
249
|
+
|
|
250
|
+
const ORIGINAL_CLONE_CAMPAIGN_ID = 1925;
|
|
251
|
+
const appsflyerAvailability = app.platforms[platform].appsflyerParams?.apiToken ? "[AF]" : "";
|
|
252
|
+
let name = `[${app.id}] ${app.name} ET [${app.bundle}] [Android] ${appsflyerAvailability} ${app.domainParams.name}`
|
|
253
|
+
let platformName = platform ? getPlatformName(platform) : null;
|
|
254
|
+
const platformCampaignName = `#${app.id} [➥] (${platformName})`;
|
|
255
|
+
const generateAlias = () => {
|
|
256
|
+
const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
|
257
|
+
return Array.from(
|
|
258
|
+
{ length: 8 },
|
|
259
|
+
() => chars.charAt(Math.floor(Math.random() * chars.length))
|
|
260
|
+
).join('');
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
let allCampaigns = await getAllCampaigns();
|
|
264
|
+
let matchingCampaign: IKeitaroCampaign[] = [];
|
|
265
|
+
|
|
266
|
+
if (platform && platform !== EPlatform.GENERAL) {
|
|
267
|
+
// Для конкретної платформи
|
|
268
|
+
const platformName = getPlatformName(platform);
|
|
269
|
+
matchingCampaign = allCampaigns.filter((c) => {
|
|
270
|
+
// Точна перевірка ID з межами слів
|
|
271
|
+
const idPattern = new RegExp(`${app.id}\\b`);
|
|
272
|
+
const hasId = idPattern.test(c.name);
|
|
273
|
+
const hasBundle = c.name.includes(`[${app.bundle}]`);
|
|
274
|
+
|
|
275
|
+
// Перевіряємо наявність платформи в дужках в кінці назви
|
|
276
|
+
const platformPattern = new RegExp(`\\(.*${platformName}.*\\)\\s*$`);
|
|
277
|
+
const hasPlatform = platformPattern.test(c.name);
|
|
278
|
+
return hasId && hasBundle && hasPlatform;
|
|
279
|
+
});
|
|
280
|
+
} else {
|
|
281
|
+
// Для General платформи
|
|
282
|
+
matchingCampaign = allCampaigns.filter((c) => {
|
|
283
|
+
// Точна перевірка ID з межами слів
|
|
284
|
+
const idPattern = new RegExp(`${app.id}\\b`);
|
|
285
|
+
const hasId = idPattern.test(c.name);
|
|
286
|
+
const hasBundle = c.name.includes(`[${app.bundle}]`);
|
|
287
|
+
|
|
288
|
+
// Перевіряємо відсутність платформи в дужках в кінці
|
|
289
|
+
// Дозволяємо будь-які інші дужки (як для гео або опису)
|
|
290
|
+
const noPlatformAtEnd = !/\(.*(?:iOS|Android|Desktop|Mobile).*\)\s*$/.test(c.name);
|
|
291
|
+
return hasId && hasBundle && noPlatformAtEnd;
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
const originalCampaign: IKeitaroCampaign = await getCampaignById(ORIGINAL_CLONE_CAMPAIGN_ID);
|
|
298
|
+
const maxPosition = Math.max(...allCampaigns.map(c => c.position || 0));
|
|
299
|
+
|
|
300
|
+
let allDomains = await getDomains(true);
|
|
301
|
+
if (!allDomains) {
|
|
302
|
+
throw Error(`Failed to get all domains list`);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
const maxGroupId = Math.max(...allCampaigns.map(c => c.group_id || 0));
|
|
307
|
+
const originalStreams = await getStreamsByCampaignId(ORIGINAL_CLONE_CAMPAIGN_ID);
|
|
308
|
+
|
|
309
|
+
let parameters = await TrafleKeitaroParameters({bundle: app.bundle, appsflyerDevKey: app.platforms[platform].appsflyerParams?.apiToken, name: name})
|
|
310
|
+
|
|
311
|
+
const createStreamWithoutIds = async (stream: IKeitaroStream, campaignId: number) => {
|
|
312
|
+
// Створюємо базовий об'єкт потоку без ID
|
|
313
|
+
const newStream = {
|
|
314
|
+
type: stream.type,
|
|
315
|
+
name: stream.name,
|
|
316
|
+
campaign_id: campaignId,
|
|
317
|
+
position: stream.position,
|
|
318
|
+
state: stream.state,
|
|
319
|
+
action_type: stream.action_type,
|
|
320
|
+
action_payload: stream.action_payload || "",
|
|
321
|
+
schema: stream.schema,
|
|
322
|
+
collect_clicks: stream.collect_clicks,
|
|
323
|
+
filter_or: stream.filter_or,
|
|
324
|
+
weight: stream.weight,
|
|
325
|
+
filters: stream.filters.map(filter => ({
|
|
326
|
+
name: filter.name,
|
|
327
|
+
mode: filter.mode,
|
|
328
|
+
payload: filter.payload
|
|
329
|
+
})),
|
|
330
|
+
offers: stream.offers.map(offer => ({
|
|
331
|
+
offer_id: offer.offer_id,
|
|
332
|
+
state: offer.state,
|
|
333
|
+
share: offer.share
|
|
334
|
+
}))
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
// Створюємо новий потік
|
|
338
|
+
await keitaroApi.post('streams', newStream);
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
const updateOrCreateCLOStream = async (campaignId: number, existingStreams?: IKeitaroStream[]) => {
|
|
342
|
+
const cloTemplate = originalStreams.find(s => s.name === "CLO");
|
|
343
|
+
if (!cloTemplate) return;
|
|
344
|
+
|
|
345
|
+
const cloStream = {
|
|
346
|
+
type: cloTemplate.type,
|
|
347
|
+
name: "CLO",
|
|
348
|
+
campaign_id: campaignId,
|
|
349
|
+
position: cloTemplate.position,
|
|
350
|
+
state: cloTemplate.state,
|
|
351
|
+
action_type: cloTemplate.action_type,
|
|
352
|
+
action_payload: cloTemplate.action_payload || "",
|
|
353
|
+
schema: cloTemplate.schema,
|
|
354
|
+
collect_clicks: cloTemplate.collect_clicks,
|
|
355
|
+
filter_or: cloTemplate.filter_or,
|
|
356
|
+
weight: cloTemplate.weight,
|
|
357
|
+
filters: [
|
|
358
|
+
{
|
|
359
|
+
name: "country",
|
|
360
|
+
mode: "reject",
|
|
361
|
+
payload: app.platforms[platform].geo || []
|
|
362
|
+
},
|
|
363
|
+
{
|
|
364
|
+
name: "bot",
|
|
365
|
+
mode: "accept",
|
|
366
|
+
payload: null
|
|
367
|
+
}
|
|
368
|
+
],
|
|
369
|
+
offers: []
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
if (existingStreams) {
|
|
373
|
+
const existingCLO = existingStreams.find(s => s.name === "CLO");
|
|
374
|
+
if (existingCLO) {
|
|
375
|
+
await keitaroApi.put(`streams/${existingCLO.id}`, cloStream);
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
await keitaroApi.post('streams', cloStream);
|
|
381
|
+
};
|
|
382
|
+
|
|
383
|
+
// Якщо знайдена існуюча кампанія
|
|
384
|
+
if (matchingCampaign.length > 0) {
|
|
385
|
+
const existingCampaign = matchingCampaign[0];
|
|
386
|
+
const existingStreams = await getStreamsByCampaignId(existingCampaign.id);
|
|
387
|
+
|
|
388
|
+
// Завжди оновлюємо CLO потік
|
|
389
|
+
await updateOrCreateCLOStream(existingCampaign.id, existingStreams);
|
|
390
|
+
|
|
391
|
+
// Для інших потоків
|
|
392
|
+
if (addDefaultStreams) {
|
|
393
|
+
for (const stream of originalStreams) {
|
|
394
|
+
if (stream.name === "CLO") continue; // Пропускаємо CLO
|
|
395
|
+
|
|
396
|
+
const existingStream = existingStreams.find(s => s.name === stream.name);
|
|
397
|
+
if (!existingStream) {
|
|
398
|
+
await createStreamWithoutIds(stream, existingCampaign.id);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
return existingCampaign;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// Створення нової кампанії
|
|
407
|
+
let alias = generateAlias();
|
|
408
|
+
|
|
409
|
+
let payload: Partial<IKeitaroCampaign> = {
|
|
410
|
+
// Унікальні поля
|
|
411
|
+
name: platformName ? platformCampaignName : name,
|
|
412
|
+
alias: alias,
|
|
413
|
+
domain_id: originalCampaign.domain_id,
|
|
414
|
+
position: [maxPosition + 100],
|
|
415
|
+
group_id: maxGroupId + 1,
|
|
416
|
+
traffic_source_id: 17,
|
|
417
|
+
parameters: (parameters ? parameters.parameters : originalCampaign.parameters) as IKeitaroCampaignParameters,
|
|
418
|
+
source: originalCampaign.source,
|
|
419
|
+
bind_visitors: "slo",
|
|
420
|
+
// Неунікальні поля з оригінальної кампанії
|
|
421
|
+
type: originalCampaign.type,
|
|
422
|
+
state: originalCampaign.state,
|
|
423
|
+
cost_type: originalCampaign.cost_type,
|
|
424
|
+
cost_value: originalCampaign.cost_value,
|
|
425
|
+
cost_currency: originalCampaign.cost_currency,
|
|
426
|
+
uniqueness_period: originalCampaign.uniqueness_period,
|
|
427
|
+
cookies_ttl: originalCampaign.cookies_ttl,
|
|
428
|
+
notes: originalCampaign.notes,
|
|
429
|
+
collect_clicks: originalCampaign.collect_clicks,
|
|
430
|
+
uniqueness_type: originalCampaign.uniqueness_type,
|
|
431
|
+
};
|
|
432
|
+
|
|
433
|
+
const newCampaign: IKeitaroCampaign = await createCampaign(payload);
|
|
434
|
+
|
|
435
|
+
// Спочатку створюємо CLO потік з оновленими гео
|
|
436
|
+
await updateOrCreateCLOStream(newCampaign.id);
|
|
437
|
+
|
|
438
|
+
// Потім клонуємо всі інші потоки
|
|
439
|
+
for (const stream of originalStreams) {
|
|
440
|
+
if (stream.name === "CLO") continue; // Пропускаємо CLO потік
|
|
441
|
+
await createStreamWithoutIds(stream, newCampaign.id);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
await keitaroApi.put(`/campaigns/${newCampaign.id}`, {
|
|
445
|
+
group_id: originalCampaign.group_id
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
const updatedCampaign = await getCampaignById(newCampaign.id);
|
|
449
|
+
return updatedCampaign;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
|
|
150
453
|
export const TraffleKeitaroService = {
|
|
151
|
-
addOffersToTraffleKeitaro, getTraffleOffersGroups, getTraffleAffiliateNetworks, createGroup, deleteOfferById, getAllOffers, updateOfferLinkById
|
|
454
|
+
addOffersToTraffleKeitaro, getTraffleOffersGroups, getTraffleAffiliateNetworks, createGroup, deleteOfferById, getAllOffers, updateOfferLinkById, cloneTraffleCampaign
|
|
152
455
|
}
|
package/test-keitaro.js
CHANGED
|
@@ -1,8 +1,33 @@
|
|
|
1
|
-
const {
|
|
1
|
+
const { TraffleKeitaroService } = require('./lib/network/keitaro/traffle/traffle-keitaro-service');
|
|
2
|
+
const { EPlatform } = require('./lib/app/app');
|
|
2
3
|
|
|
3
4
|
async function testClone() {
|
|
4
5
|
try {
|
|
5
|
-
|
|
6
|
+
// Тестові дані для створення кампанії
|
|
7
|
+
const testApp = {
|
|
8
|
+
id: 901,
|
|
9
|
+
bundle: "com.test.app",
|
|
10
|
+
name: "Test App",
|
|
11
|
+
platforms: {
|
|
12
|
+
'@': {
|
|
13
|
+
geo: ["UA", "RU"],
|
|
14
|
+
appsflyerParams: {
|
|
15
|
+
apiToken: "test_token"
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
domainParams: {
|
|
20
|
+
name: "test.com"
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
// Створюємо Traffle кампанію
|
|
25
|
+
const result = await TraffleKeitaroService.cloneTraffleCampaign(
|
|
26
|
+
testApp,
|
|
27
|
+
EPlatform.GENERAL,
|
|
28
|
+
true // addDefaultStreams
|
|
29
|
+
);
|
|
30
|
+
|
|
6
31
|
console.log('Cloned campaign:', result);
|
|
7
32
|
} catch (error) {
|
|
8
33
|
console.error('Error:', error);
|
package/tsc
ADDED
|
File without changes
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import { KeitaroClicksInterval } from './keitaro-clicks';
|
|
2
|
-
export declare enum KeitaroOperator {
|
|
3
|
-
EQUALS = "EQUALS",
|
|
4
|
-
NOT_EQUAL = "NOT_EQUAL",
|
|
5
|
-
EQUALS_OR_GREATER_THAN = "EQUALS_OR_GREATER_THAN",
|
|
6
|
-
EQUALS_OR_LESS_THAN = "EQUALS_OR_LESS_THAN",
|
|
7
|
-
GREATER_THAN = "GREATER_THAN",
|
|
8
|
-
LESS_THAN = "LESS_THAN",
|
|
9
|
-
MATCH_REGEXP = "MATCH_REGEXP",
|
|
10
|
-
NOT_MATCH_REGEXP = "NOT_MATCH_REGEXP",
|
|
11
|
-
BEGINS_WITH = "BEGINS_WITH",
|
|
12
|
-
ENDS_WITH = "ENDS_WITH",
|
|
13
|
-
CONTAINS = "CONTAINS",
|
|
14
|
-
NOT_CONTAIN = "NOT_CONTAIN",
|
|
15
|
-
IN_LIST = "IN_LIST",
|
|
16
|
-
NOT_IN_LIST = "NOT_IN_LIST",
|
|
17
|
-
BETWEEN = "BETWEEN",
|
|
18
|
-
IS_SET = "IS_SET",
|
|
19
|
-
IS_NOT_SET = "IS_NOT_SET",
|
|
20
|
-
IS_TRUE = "IS_TRUE",
|
|
21
|
-
IS_FALSE = "IS_FALSE",
|
|
22
|
-
HAS_LABEL = "HAS_LABEL",
|
|
23
|
-
NOT_HAS_LABEL = "NOT_HAS_LABEL"
|
|
24
|
-
}
|
|
25
|
-
export interface IKeitaroConversionRangeRequest {
|
|
26
|
-
from?: string;
|
|
27
|
-
to?: string;
|
|
28
|
-
timezone?: string;
|
|
29
|
-
interval?: KeitaroClicksInterval;
|
|
30
|
-
}
|
|
31
|
-
export interface IKeitaroConversionFilterRequest {
|
|
32
|
-
name: string;
|
|
33
|
-
operator: KeitaroOperator;
|
|
34
|
-
expression?: string | number | Array<string | number>;
|
|
35
|
-
}
|
|
36
|
-
export interface IKeitaroConversionSortRequest {
|
|
37
|
-
name: string;
|
|
38
|
-
order: 'ASC' | 'DESC';
|
|
39
|
-
}
|
|
40
|
-
export interface IKeitaroConversionsRequest {
|
|
41
|
-
range: IKeitaroConversionRangeRequest;
|
|
42
|
-
limit?: number;
|
|
43
|
-
offset?: number;
|
|
44
|
-
columns?: string[];
|
|
45
|
-
filters?: IKeitaroConversionFilterRequest[];
|
|
46
|
-
sort?: IKeitaroConversionSortRequest[];
|
|
47
|
-
}
|
|
48
|
-
export interface IKeitaroConversionsResponse {
|
|
49
|
-
rows: any[];
|
|
50
|
-
total: number;
|
|
51
|
-
meta: string[];
|
|
52
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.KeitaroOperator = void 0;
|
|
4
|
-
var KeitaroOperator;
|
|
5
|
-
(function (KeitaroOperator) {
|
|
6
|
-
KeitaroOperator["EQUALS"] = "EQUALS";
|
|
7
|
-
KeitaroOperator["NOT_EQUAL"] = "NOT_EQUAL";
|
|
8
|
-
KeitaroOperator["EQUALS_OR_GREATER_THAN"] = "EQUALS_OR_GREATER_THAN";
|
|
9
|
-
KeitaroOperator["EQUALS_OR_LESS_THAN"] = "EQUALS_OR_LESS_THAN";
|
|
10
|
-
KeitaroOperator["GREATER_THAN"] = "GREATER_THAN";
|
|
11
|
-
KeitaroOperator["LESS_THAN"] = "LESS_THAN";
|
|
12
|
-
KeitaroOperator["MATCH_REGEXP"] = "MATCH_REGEXP";
|
|
13
|
-
KeitaroOperator["NOT_MATCH_REGEXP"] = "NOT_MATCH_REGEXP";
|
|
14
|
-
KeitaroOperator["BEGINS_WITH"] = "BEGINS_WITH";
|
|
15
|
-
KeitaroOperator["ENDS_WITH"] = "ENDS_WITH";
|
|
16
|
-
KeitaroOperator["CONTAINS"] = "CONTAINS";
|
|
17
|
-
KeitaroOperator["NOT_CONTAIN"] = "NOT_CONTAIN";
|
|
18
|
-
KeitaroOperator["IN_LIST"] = "IN_LIST";
|
|
19
|
-
KeitaroOperator["NOT_IN_LIST"] = "NOT_IN_LIST";
|
|
20
|
-
KeitaroOperator["BETWEEN"] = "BETWEEN";
|
|
21
|
-
KeitaroOperator["IS_SET"] = "IS_SET";
|
|
22
|
-
KeitaroOperator["IS_NOT_SET"] = "IS_NOT_SET";
|
|
23
|
-
KeitaroOperator["IS_TRUE"] = "IS_TRUE";
|
|
24
|
-
KeitaroOperator["IS_FALSE"] = "IS_FALSE";
|
|
25
|
-
KeitaroOperator["HAS_LABEL"] = "HAS_LABEL";
|
|
26
|
-
KeitaroOperator["NOT_HAS_LABEL"] = "NOT_HAS_LABEL";
|
|
27
|
-
})(KeitaroOperator = exports.KeitaroOperator || (exports.KeitaroOperator = {}));
|
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.displayComparisonResults = void 0;
|
|
4
|
-
function displayComparisonResults(comparison) {
|
|
5
|
-
const isValidStatus = (status) => ['sale', 'rebill'].includes(status);
|
|
6
|
-
let wpTotalSum = comparison.wpConversions
|
|
7
|
-
.reduce((sum, conv) => sum + conv.sum, 0);
|
|
8
|
-
let keitaroTotalSum = comparison.keitaroConversions
|
|
9
|
-
.filter(conv => isValidStatus(conv.status))
|
|
10
|
-
.reduce((sum, conv) => sum + (parseFloat(conv.revenue) || 0), 0);
|
|
11
|
-
// Get all Keitaro rejected conversions
|
|
12
|
-
const allKeitaroRejected = [
|
|
13
|
-
...comparison.rejected.keitaro,
|
|
14
|
-
...comparison.matches
|
|
15
|
-
.filter(match => match.keitaro.status === 'rejected')
|
|
16
|
-
.map(match => match.keitaro)
|
|
17
|
-
];
|
|
18
|
-
// Group mismatches by project/offer
|
|
19
|
-
const wpMismatchesByProject = new Map();
|
|
20
|
-
const keitaroMismatchesByOffer = new Map();
|
|
21
|
-
const rejectedByOffer = new Map();
|
|
22
|
-
comparison.mismatches.onlyInWP.forEach(conv => {
|
|
23
|
-
if (!wpMismatchesByProject.has(conv.project)) {
|
|
24
|
-
wpMismatchesByProject.set(conv.project, { count: 0, sum: 0, conversions: [] });
|
|
25
|
-
}
|
|
26
|
-
const projectStats = wpMismatchesByProject.get(conv.project);
|
|
27
|
-
projectStats.count++;
|
|
28
|
-
projectStats.sum += conv.sum;
|
|
29
|
-
projectStats.conversions.push(conv);
|
|
30
|
-
});
|
|
31
|
-
// Include all conversions in mismatches, including rejected ones
|
|
32
|
-
[...comparison.mismatches.onlyInKeitaro, ...allKeitaroRejected].forEach(conv => {
|
|
33
|
-
if (!keitaroMismatchesByOffer.has(conv.offer)) {
|
|
34
|
-
keitaroMismatchesByOffer.set(conv.offer, { count: 0, sum: 0, conversions: [] });
|
|
35
|
-
}
|
|
36
|
-
const offerStats = keitaroMismatchesByOffer.get(conv.offer);
|
|
37
|
-
offerStats.count++;
|
|
38
|
-
offerStats.sum += parseFloat(conv.revenue) || 0;
|
|
39
|
-
offerStats.conversions.push(conv);
|
|
40
|
-
});
|
|
41
|
-
// Group rejected conversions by offer
|
|
42
|
-
allKeitaroRejected.forEach(conv => {
|
|
43
|
-
if (!rejectedByOffer.has(conv.offer)) {
|
|
44
|
-
rejectedByOffer.set(conv.offer, { count: 0, sum: 0, conversions: [] });
|
|
45
|
-
}
|
|
46
|
-
const offerStats = rejectedByOffer.get(conv.offer);
|
|
47
|
-
offerStats.count++;
|
|
48
|
-
offerStats.sum += parseFloat(conv.revenue) || 0;
|
|
49
|
-
offerStats.conversions.push(conv);
|
|
50
|
-
});
|
|
51
|
-
console.log('\n\x1b[1mЗагальна статистика:\x1b[0m');
|
|
52
|
-
console.log(`Всього конверсій у WP: ${comparison.wpConversions.length} (Сума: \x1b[33m$${wpTotalSum.toFixed(2)}\x1b[0m)`);
|
|
53
|
-
console.log(`Всього конверсій у Keitaro: ${comparison.keitaroConversions.filter(conv => isValidStatus(conv.status)).length} (Сума: \x1b[33m$${keitaroTotalSum.toFixed(2)}\x1b[0m)`);
|
|
54
|
-
console.log(`Співпадінь: ${comparison.matches.length}`);
|
|
55
|
-
console.log(`Відхилених конверсій: ${allKeitaroRejected.length}`);
|
|
56
|
-
const totalDiff = Math.abs(wpTotalSum - keitaroTotalSum);
|
|
57
|
-
console.log(`\x1b[1mЗагальна різниця:\x1b[0m \x1b[33m$${totalDiff.toFixed(2)}\x1b[0m`);
|
|
58
|
-
// Always show rejected conversions first if there are any
|
|
59
|
-
if (allKeitaroRejected.length > 0) {
|
|
60
|
-
const rejectedTotal = allKeitaroRejected.reduce((sum, conv) => sum + (parseFloat(conv.revenue) || 0), 0);
|
|
61
|
-
console.log('\n\x1b[1mВідхилені конверсії в Keitaro за офферами:\x1b[0m');
|
|
62
|
-
console.log(`Загальна кількість: ${allKeitaroRejected.length}`);
|
|
63
|
-
console.log(`Загальна сума: \x1b[33m$${rejectedTotal.toFixed(2)}\x1b[0m`);
|
|
64
|
-
for (const [offer, stats] of rejectedByOffer.entries()) {
|
|
65
|
-
console.log(`\n\x1b[33m${offer}:\x1b[0m`);
|
|
66
|
-
console.log(`Кількість: ${stats.count}, Сума: $${stats.sum.toFixed(2)}`);
|
|
67
|
-
stats.conversions.forEach(conv => {
|
|
68
|
-
const matchMark = comparison.matches.some(match => match.keitaro.sub_id === conv.sub_id) ? '[matched] ' : '';
|
|
69
|
-
console.log(` ${matchMark}${conv.sub_id} | ${conv.sale_datetime.split(' ')[0]} | $${conv.revenue}`);
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
console.log('\n\x1b[1mВідсутні в Keitaro за проектами:\x1b[0m');
|
|
74
|
-
for (const [project, stats] of wpMismatchesByProject) {
|
|
75
|
-
console.log(`\n\x1b[31m${project}:\x1b[0m`);
|
|
76
|
-
console.log(`Кількість: ${stats.count}, Сума: $${stats.sum.toFixed(2)}`);
|
|
77
|
-
stats.conversions.forEach(conv => {
|
|
78
|
-
console.log(` ${conv.clickId} | ${conv.regDate.toISOString().split('T')[0]} | $${conv.sum}`);
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
console.log('\n\x1b[1mВідсутні в WP за офферами:\x1b[0m');
|
|
82
|
-
for (const [offer, stats] of keitaroMismatchesByOffer) {
|
|
83
|
-
console.log(`\n\x1b[32m${offer}:\x1b[0m`);
|
|
84
|
-
console.log(`Кількість: ${stats.count}, Сума: $${stats.sum.toFixed(2)}`);
|
|
85
|
-
stats.conversions.forEach(conv => {
|
|
86
|
-
const statusMark = conv.status === 'rejected' ? '(rejected) ' : '';
|
|
87
|
-
const matchMark = comparison.matches.some(match => match.keitaro.sub_id === conv.sub_id) ? '[matched] ' : '';
|
|
88
|
-
console.log(` ${statusMark}${matchMark}${conv.sub_id} | ${conv.sale_datetime.split(' ')[0]} | $${conv.revenue}`);
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
let mismatchWPSum = comparison.mismatches.onlyInWP
|
|
92
|
-
.reduce((sum, conv) => sum + conv.sum, 0);
|
|
93
|
-
let mismatchKeitaroSum = [...comparison.mismatches.onlyInKeitaro, ...allKeitaroRejected]
|
|
94
|
-
.reduce((sum, conv) => sum + (parseFloat(conv.revenue) || 0), 0);
|
|
95
|
-
console.log('\n\x1b[1mПідсумок розбіжностей:\x1b[0m');
|
|
96
|
-
console.log(`Сума відсутніх в Keitaro: \x1b[31m$${mismatchWPSum.toFixed(2)}\x1b[0m`);
|
|
97
|
-
console.log(`Сума відсутніх в WP: \x1b[32m$${mismatchKeitaroSum.toFixed(2)}\x1b[0m`);
|
|
98
|
-
console.log(`Різниця у відсутніх конверсіях: \x1b[33m$${Math.abs(mismatchWPSum - mismatchKeitaroSum).toFixed(2)}\x1b[0m`);
|
|
99
|
-
// Debug information
|
|
100
|
-
const matchingRejected = comparison.matches.filter(match => match.keitaro.status === 'rejected').length;
|
|
101
|
-
console.log('\n\x1b[1mДебаг інформація:\x1b[0m');
|
|
102
|
-
console.log(`Кількість конверсій відсутніх в WP: ${comparison.mismatches.onlyInKeitaro.length + comparison.rejected.keitaro.length + matchingRejected}`);
|
|
103
|
-
console.log(`З них:`);
|
|
104
|
-
console.log(`- Активних: ${comparison.mismatches.onlyInKeitaro.length}`);
|
|
105
|
-
console.log(`- Відхилених (унікальних): ${comparison.rejected.keitaro.length}`);
|
|
106
|
-
console.log(`- Відхилених (що є в WP): ${matchingRejected}`);
|
|
107
|
-
}
|
|
108
|
-
exports.displayComparisonResults = displayComparisonResults;
|
package/lib/keitaro-utils.d.ts
DELETED