@bprotsyk/aso-core 2.1.223 → 2.1.225

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.
@@ -3,13 +3,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.KeitaroService = void 0;
6
+ exports.KeitaroService = exports.upsertStreamToCampaign = void 0;
7
7
  const http_1 = __importDefault(require("./http"));
8
8
  const app_1 = require("../../app/app");
9
- // Default group IDs
10
- const DEFAULT_OFFERS_GROUP_ID = 107;
11
- const DEFAULT_CAMPAIGNS_GROUP_ID = 106;
12
- // Basic campaign operations
9
+ const keitaro_utils_1 = require("../../utils/keitaro-utils");
10
+ const general_1 = require("../../utils/general");
13
11
  async function getStreamsByCampaignId(campaignId) {
14
12
  const { data: streams } = await http_1.default.get(`campaigns/${campaignId}/streams`);
15
13
  return streams;
@@ -18,175 +16,239 @@ async function getAllCampaigns() {
18
16
  const { data: campaigns } = await http_1.default.get('campaigns');
19
17
  return campaigns;
20
18
  }
21
- async function getCampaignById(id) {
22
- const { data: campaign } = await http_1.default.get(`/campaigns/${id}`);
23
- return campaign;
19
+ async function cloneStreams(originalCampaignId, streamPositionsToClone, campaignRegExp) {
20
+ // Get the original campaign's streams
21
+ const originalStreams = await getStreamsByCampaignId(originalCampaignId);
22
+ // Filter the original streams by the given stream positions to clone
23
+ const streamsToClone = originalStreams.filter(stream => streamPositionsToClone.includes(stream.position));
24
+ // Get a list of all campaigns
25
+ const allCampaigns = await getAllCampaigns();
26
+ // Filter the campaigns by the given RegExp
27
+ const matchingCampaigns = allCampaigns.filter(campaign => campaignRegExp.exec(campaign.name) && campaign.id != originalCampaignId);
28
+ // console.log(matchingCampaigns)
29
+ // For each matching campaign, clone the streams
30
+ for (const matchingCampaign of matchingCampaigns) {
31
+ for (const streamToClone of streamsToClone) {
32
+ await http_1.default.post('streams', {
33
+ name: streamToClone.name,
34
+ campaign_id: matchingCampaign.id,
35
+ schema: streamToClone.schema,
36
+ type: streamToClone.type,
37
+ action_type: streamToClone.action_type,
38
+ weight: 100,
39
+ offers: streamToClone.offers.map((offer) => {
40
+ return {
41
+ offer_id: offer.offer_id,
42
+ share: 100
43
+ };
44
+ }),
45
+ filters: streamToClone.filters.map((filter) => {
46
+ return {
47
+ name: filter.name,
48
+ mode: filter.mode,
49
+ payload: filter.payload
50
+ };
51
+ }),
52
+ });
53
+ }
54
+ }
24
55
  }
25
56
  async function updateCampaign(id, payload) {
26
57
  await http_1.default.put(`campaigns/${id}`, payload);
27
58
  }
28
- async function createCampaign(campaignData) {
29
- let { data: campaign } = await http_1.default.post(`/campaigns`, campaignData);
30
- return campaign;
31
- }
32
- // Domain operations
33
- async function getDomains(onlyActive) {
34
- const { data: domains } = await http_1.default.get(`/domains`);
35
- return onlyActive ? domains.filter((d) => d.network_status == "active") : domains;
59
+ async function createStreamForMatchingCampaigns(streamPayloads, campaignRegExp, avoidGroup) {
60
+ // Get all campaigns once
61
+ const allCampaigns = await getAllCampaigns();
62
+ const matchingCampaigns = allCampaigns.filter(campaign => (avoidGroup ? campaign.group_id != avoidGroup : true) &&
63
+ campaignRegExp.exec(campaign.name));
64
+ // Get all streams for matching campaigns once
65
+ const campaignStreams = new Map();
66
+ for (const campaign of matchingCampaigns) {
67
+ campaignStreams.set(campaign.id, await getStreamsByCampaignId(campaign.id));
68
+ }
69
+ // Process each campaign
70
+ for (const matchingCampaign of matchingCampaigns) {
71
+ const streams = campaignStreams.get(matchingCampaign.id) || [];
72
+ // Process each stream payload
73
+ for (const { payload, offerId } of streamPayloads) {
74
+ const identicalStream = streams.find(stream => stream.name.includes(offerId));
75
+ try {
76
+ if (identicalStream) {
77
+ await http_1.default.put(`streams/${identicalStream.id}`, {
78
+ campaign_id: matchingCampaign.id,
79
+ ...payload
80
+ });
81
+ }
82
+ else {
83
+ await http_1.default.post('streams', {
84
+ campaign_id: matchingCampaign.id,
85
+ ...payload
86
+ });
87
+ }
88
+ }
89
+ catch (error) {
90
+ console.error(`Error updating stream ${offerId} (${payload.name}) in campaign ${matchingCampaign.id} (${matchingCampaign.name}):`, error);
91
+ }
92
+ }
93
+ }
36
94
  }
37
- // Offer operations
38
95
  async function getAllOffers() {
39
96
  const { data: offers } = await http_1.default.get('offers');
40
97
  return offers;
41
98
  }
42
- /**
43
- * Create offer in Keitaro from our IOffer
44
- * @param offer - Our internal IOffer object
45
- * @returns Keitaro offer with ID
46
- */
47
- async function createOfferInKeitaro(offer) {
48
- const payload = {
49
- name: `${offer.caption} ${offer.geo} (${offer.name})`,
50
- group_id: DEFAULT_OFFERS_GROUP_ID,
51
- action_type: 'http',
52
- action_payload: offer.link,
53
- affiliate_network_id: 0,
54
- payout_value: 0,
55
- payout_currency: 'USD',
56
- payout_type: 'CPA',
57
- state: offer.hidden ? 'disabled' : 'active',
58
- country: [offer.geo],
59
- offer_type: 'external',
60
- payout_auto: true,
61
- payout_upsell: true,
62
- };
63
- const { data: keitaroOffer } = await http_1.default.post('offers', payload);
64
- return keitaroOffer;
99
+ async function findKeitaroOffers(filter) {
100
+ let offers = await getAllOffers();
101
+ if (filter.caption)
102
+ offers = offers.filter((o) => o.name.includes(filter.caption));
103
+ if (filter.keitaroId)
104
+ offers = offers.filter((o) => o.id == filter.keitaroId);
105
+ if (filter.name)
106
+ offers = offers.filter((o) => o.name.includes(filter.name));
107
+ return offers;
65
108
  }
66
- // Stream operations
67
- async function createStream(campaignId, streamData) {
68
- const { data: stream } = await http_1.default.post('streams', {
69
- ...streamData,
70
- campaign_id: campaignId
71
- });
72
- return stream;
109
+ async function getOfferByKeitaroId(id) {
110
+ const { data: offer } = await http_1.default.get(`offers/${id}`);
111
+ return offer;
73
112
  }
74
- /**
75
- * Get all offerwall campaigns (campaigns with [◈] prefix in group 106)
76
- */
77
- async function getOfferwallCampaigns() {
78
- const allCampaigns = await getAllCampaigns();
79
- // Offerwall campaigns have [◈] in their name and are in group 106
80
- return allCampaigns.filter(c => c.name.includes('[◈]') && c.group_id === DEFAULT_CAMPAIGNS_GROUP_ID);
113
+ async function updateOffer(offer) {
114
+ const { data: o } = await http_1.default.put(`offers/${offer.id}`, offer);
115
+ return o;
81
116
  }
82
- /**
83
- * Add our IOffer to Keitaro and distribute to all offerwall campaigns
84
- * @param offer - Our internal IOffer object
85
- * @returns Result with Keitaro offer ID and distribution stats
86
- */
87
- async function addOfferToKeitaro(offer) {
88
- // First, create the offer in Keitaro
89
- const keitaroOffer = await createOfferInKeitaro(offer);
90
- console.log(`Created Keitaro offer #${keitaroOffer.id}: ${keitaroOffer.name}`);
91
- // Then distribute to all offerwall campaigns
92
- const offerwallCampaigns = await getOfferwallCampaigns();
93
- let success = 0;
94
- let failed = 0;
95
- const campaignNames = [];
96
- for (const campaign of offerwallCampaigns) {
97
- try {
98
- // Check if stream with this offer already exists
99
- const existingStreams = await getStreamsByCampaignId(campaign.id);
100
- const streamExists = existingStreams.some(stream => stream.offers?.some(o => o.offer_id === keitaroOffer.id));
101
- if (streamExists) {
102
- console.log(`Stream with offer ${keitaroOffer.id} already exists in campaign ${campaign.name}`);
103
- continue;
104
- }
105
- // Create a new stream with the offer
106
- await createStream(campaign.id, {
107
- name: `${offer.geo} - ${offer.caption} (${offer.name})`,
108
- type: 'regular',
109
- action_type: 'http',
110
- weight: 100,
111
- state: offer.hidden ? 'disabled' : 'active',
112
- offers: [{
113
- offer_id: keitaroOffer.id,
114
- share: 100,
115
- state: 'active'
116
- }],
117
- filters: [{
118
- name: 'country',
119
- mode: 'accept',
120
- payload: [offer.geo]
121
- }]
122
- });
123
- success++;
124
- campaignNames.push(campaign.name);
117
+ function createStreamPartialPayload(keitaroOfferId, offerName, offerId, offerGeo) {
118
+ return {
119
+ name: `${offerName} ${offerGeo} (${offerId})`,
120
+ schema: "landings",
121
+ type: "regular",
122
+ action_type: "http",
123
+ weight: 100,
124
+ offers: [{
125
+ offer_id: keitaroOfferId,
126
+ share: 100,
127
+ state: "active"
128
+ }],
129
+ filters: [{
130
+ name: "sub_id_15",
131
+ mode: "accept",
132
+ payload: [offerId]
133
+ }],
134
+ };
135
+ }
136
+ async function addOffersToKeitaro(offers, affiliateId, links, avoidGroup, groupId) {
137
+ const allOffers = await getAllOffers();
138
+ const streamPayloads = [];
139
+ for (let i = 0; i < offers.length; i++) {
140
+ const offer = offers[i];
141
+ const link = links[i];
142
+ const identicalOffer = allOffers.find(o => o.name.includes(offer.name));
143
+ let keitaroOfferId;
144
+ if (identicalOffer) {
145
+ keitaroOfferId = identicalOffer.id;
125
146
  }
126
- catch (error) {
127
- console.error(`Failed to add offer to campaign ${campaign.name}:`, error);
128
- failed++;
147
+ else {
148
+ const offerPayload = {
149
+ name: `${offer.caption} ${offer.geo} (${offer.name})`,
150
+ group_id: groupId || 1,
151
+ action_payload: link,
152
+ affiliate_network_id: affiliateId,
153
+ country: [offer.geo],
154
+ action_type: "http",
155
+ offer_type: "external",
156
+ payout_auto: true,
157
+ payout_upsell: true,
158
+ };
159
+ const { data: keitaroOffer } = await http_1.default.post('offers', offerPayload);
160
+ keitaroOfferId = keitaroOffer.id;
129
161
  }
162
+ streamPayloads.push({
163
+ payload: createStreamPartialPayload(keitaroOfferId, offer.caption, offer.name, offer.geo),
164
+ offerId: offer.name
165
+ });
130
166
  }
131
- return { keitaroOfferId: keitaroOffer.id, success, failed, campaigns: campaignNames };
167
+ await createStreamForMatchingCampaigns(streamPayloads, /◈/, avoidGroup);
168
+ }
169
+ async function createCampaign(campaignData) {
170
+ let { data: campaign } = await http_1.default.post(`/campaigns`, campaignData);
171
+ return campaign;
132
172
  }
133
- // Generate random alias for campaigns
134
- function generateAlias() {
135
- const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
136
- return Array.from({ length: 8 }, () => chars.charAt(Math.floor(Math.random() * chars.length))).join('');
173
+ async function getCampaignById(id) {
174
+ const { data: campaign } = await http_1.default.get(`/campaigns/${id}`);
175
+ return campaign;
137
176
  }
138
- /**
139
- * Clone a campaign for an app based on integration type
140
- * @param app - The app to create campaign for
141
- * @param integrationType - OFFERWALL or DIRECT
142
- * @returns Keitaro data for the cloned campaign
143
- */
144
- async function cloneCampaignForApp(app, integrationType) {
145
- const templateId = integrationType === app_1.IntegrationType.OFFERWALL
146
- ? app_1.OFFERWALL_TEMPLATE_CAMPAIGN_ID
147
- : app_1.DIRECT_TEMPLATE_CAMPAIGN_ID;
148
- const campaignPrefix = integrationType === app_1.IntegrationType.OFFERWALL ? '[◈]' : '[➥]';
149
- const campaignName = `#${app.id} ${campaignPrefix} ${app.name || app.bundle}`;
150
- // Check if campaign already exists
151
- const allCampaigns = await getAllCampaigns();
152
- const existingCampaign = allCampaigns.find(c => c.name.includes(`#${app.id}`) && c.name.includes(campaignPrefix));
153
- if (existingCampaign) {
154
- return campaignToKeitaroData(existingCampaign);
177
+ async function upsertStreamToCampaign(campaign, stream) {
178
+ let streams = await getStreamsByCampaignId(campaign.id);
179
+ let identicalStream = streams.find((s) => stream.name == s.name);
180
+ if (identicalStream) {
181
+ console.log(`Found identical! Name: ${stream.name}`);
182
+ await http_1.default.put(`streams/${identicalStream.id}`, {
183
+ campaign_id: campaign.id,
184
+ ...stream
185
+ });
155
186
  }
156
- // Get template campaign and its streams
157
- const templateCampaign = await getCampaignById(templateId);
158
- const templateStreams = await getStreamsByCampaignId(templateId);
159
- // Get random active domain
160
- const allDomains = await getDomains(true);
161
- if (!allDomains || allDomains.length === 0) {
162
- throw new Error('No active domains available');
187
+ else {
188
+ await http_1.default.post('streams', {
189
+ campaign_id: campaign.id,
190
+ ...stream
191
+ });
163
192
  }
164
- const domain = allDomains[Math.floor(Math.random() * allDomains.length)];
165
- // Create campaign payload
193
+ }
194
+ exports.upsertStreamToCampaign = upsertStreamToCampaign;
195
+ async function cloneOWCampaign(app, platform) {
196
+ let name = `#${app.id} [◈]`;
197
+ let platformName = platform ? (0, app_1.getPlatformName)(platform) : null;
198
+ const platformCampaignName = `#${app.id} [◈] (${platformName})`;
199
+ const generateAlias = () => {
200
+ const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
201
+ return Array.from({ length: 8 }, () => chars.charAt(Math.floor(Math.random() * chars.length))).join('');
202
+ };
203
+ let allCampaigns = await getAllCampaigns();
204
+ let matchingCampaign = [];
205
+ if (platform && platform !== app_1.EPlatform.GENERAL) {
206
+ // Шукаємо кампанію з платформою
207
+ matchingCampaign = allCampaigns.filter((c) => c.name.includes(`#${app.id}`) &&
208
+ c.name.includes(`[◈]`) &&
209
+ c.name.includes(`(${platformName})`));
210
+ }
211
+ else {
212
+ // Шукаємо кампанію без платформи або з суфіксом
213
+ matchingCampaign = allCampaigns.filter((c) => c.name.includes(`#${app.id}`) &&
214
+ c.name.includes(`[◈]`) &&
215
+ !c.name.includes('('));
216
+ }
217
+ if (matchingCampaign.length > 0)
218
+ return matchingCampaign[0];
219
+ const originalCampaign = await getCampaignById(2673);
166
220
  const maxPosition = Math.max(...allCampaigns.map(c => c.position || 0));
167
- const alias = generateAlias();
168
- const payload = {
169
- name: campaignName,
221
+ const originalStreams = await getStreamsByCampaignId(2673);
222
+ let allDomains = await exports.KeitaroService.getDomains(true);
223
+ if (!allDomains) {
224
+ throw Error(`Failed to get all domains list`);
225
+ }
226
+ const domain = allDomains[Math.floor(Math.random() * allDomains.length)];
227
+ const maxGroupId = Math.max(...allCampaigns.map(c => c.group_id || 0));
228
+ let alias = generateAlias();
229
+ let payload = {
230
+ // Унікальні поля
231
+ name: platformName ? platformCampaignName : name,
170
232
  alias: alias,
171
233
  domain_id: domain.id,
172
234
  position: [maxPosition + 100],
173
- group_id: DEFAULT_CAMPAIGNS_GROUP_ID,
174
- type: templateCampaign.type,
175
- state: templateCampaign.state,
176
- cost_type: templateCampaign.cost_type,
177
- cost_value: templateCampaign.cost_value,
178
- cost_currency: templateCampaign.cost_currency,
179
- uniqueness_period: templateCampaign.uniqueness_period,
180
- cookies_ttl: templateCampaign.cookies_ttl,
181
- notes: templateCampaign.notes,
182
- collect_clicks: templateCampaign.collect_clicks,
183
- uniqueness_type: templateCampaign.uniqueness_type,
184
- parameters: templateCampaign.parameters,
235
+ group_id: maxGroupId + 1,
236
+ traffic_source_id: keitaro_utils_1.TRAFFIC_SOURCE_ID_FLASH_AI,
237
+ parameters: (0, keitaro_utils_1.prepareOWCampaignParameters)(app),
238
+ // Неунікальні поля з оригінальної кампанії
239
+ type: originalCampaign.type,
240
+ state: originalCampaign.state,
241
+ cost_type: originalCampaign.cost_type,
242
+ cost_value: originalCampaign.cost_value,
243
+ cost_currency: originalCampaign.cost_currency,
244
+ uniqueness_period: originalCampaign.uniqueness_period,
245
+ cookies_ttl: originalCampaign.cookies_ttl,
246
+ notes: originalCampaign.notes,
247
+ collect_clicks: originalCampaign.collect_clicks,
248
+ uniqueness_type: originalCampaign.uniqueness_type,
185
249
  };
186
- // Create the new campaign
187
250
  const newCampaign = await createCampaign(payload);
188
- // Clone streams from template
189
- for (const stream of templateStreams) {
251
+ for (const stream of originalStreams) {
190
252
  await http_1.default.post('streams', {
191
253
  name: stream.name,
192
254
  campaign_id: newCampaign.id,
@@ -208,53 +270,241 @@ async function cloneCampaignForApp(app, integrationType) {
208
270
  state: stream.state
209
271
  });
210
272
  }
211
- // Get updated campaign with token
273
+ await http_1.default.put(`/campaigns/${newCampaign.id}`, {
274
+ group_id: originalCampaign.group_id
275
+ });
212
276
  const updatedCampaign = await getCampaignById(newCampaign.id);
213
- return campaignToKeitaroData(updatedCampaign);
277
+ return updatedCampaign;
214
278
  }
215
- /**
216
- * Convert a Keitaro campaign to IAppKeitaroData
217
- */
218
- function campaignToKeitaroData(campaign) {
219
- return {
220
- trackingCampaignId: campaign.id,
221
- trackingCampaignName: campaign.name,
222
- trackingCampaignAlias: campaign.alias,
223
- trackingDomainId: campaign.domain_id,
224
- trackingDomainName: campaign.domain || '',
225
- campingToken: campaign.token,
226
- trackingParams: {
227
- naming: campaign.parameters?.sub_id_15?.name || 'naming',
228
- firebase_app_instance_id: campaign.parameters?.sub_id_11?.name || 'firebase_app_instance_id',
229
- firebase_user_id: campaign.parameters?.sub_id_12?.name || 'firebase_user_id',
230
- firebase_device_language_code: campaign.parameters?.sub_id_13?.name || 'firebase_device_language_code',
231
- firebase_push_token: campaign.parameters?.sub_id_14?.name || 'firebase_push_token',
232
- bundle_id: campaign.parameters?.sub_id_16?.name || 'bundle_id',
233
- advertising_id: campaign.parameters?.sub_id_17?.name || 'advertising_id',
234
- appsflyer_device_id: campaign.parameters?.sub_id_18?.name || 'appsflyer_device_id',
235
- campaign: campaign.parameters?.sub_id_20?.name || 'campaign',
279
+ async function cloneDirectCampaign(app, platform, addDefaultStreams) {
280
+ let name = `#${app.id} [➥]`;
281
+ let platformName = platform ? (0, app_1.getPlatformName)(platform) : null;
282
+ const platformCampaignName = `#${app.id} [➥] (${platformName})`;
283
+ const generateAlias = () => {
284
+ const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
285
+ return Array.from({ length: 8 }, () => chars.charAt(Math.floor(Math.random() * chars.length))).join('');
286
+ };
287
+ let allCampaigns = await getAllCampaigns();
288
+ let matchingCampaign = [];
289
+ if (platform && platform !== app_1.EPlatform.GENERAL) {
290
+ // Для конкретної платформи
291
+ const platformName = (0, app_1.getPlatformName)(platform);
292
+ matchingCampaign = allCampaigns.filter((c) => {
293
+ // Точна перевірка ID з межами слів
294
+ const idPattern = new RegExp(`#${app.id}\\b`);
295
+ const hasId = idPattern.test(c.name);
296
+ // Перевіряємо наявність стрілки (може бути в комбінації з іншими символами)
297
+ const hasArrow = c.name.includes('');
298
+ // Перевіряємо наявність платформи в дужках в кінці назви
299
+ const platformPattern = new RegExp(`\\(.*${platformName}.*\\)\\s*$`);
300
+ const hasPlatform = platformPattern.test(c.name);
301
+ return hasId && hasArrow && hasPlatform;
302
+ });
303
+ }
304
+ else {
305
+ // Для General платформи
306
+ matchingCampaign = allCampaigns.filter((c) => {
307
+ // Точна перевірка ID з межами слів
308
+ const idPattern = new RegExp(`#${app.id}\\b`);
309
+ const hasId = idPattern.test(c.name);
310
+ // Перевіряємо наявність стрілки (може бути в комбінації з іншими символами)
311
+ const hasArrow = c.name.includes('➥');
312
+ // Перевіряємо відсутність платформи в дужках в кінці
313
+ // Дозволяємо будь-які інші дужки (як для гео або опису)
314
+ const noPlatformAtEnd = !/\(.*(?:iOS|Android|Desktop|Mobile).*\)\s*$/.test(c.name);
315
+ return hasId && hasArrow && noPlatformAtEnd;
316
+ });
317
+ }
318
+ if (matchingCampaign.length > 0) {
319
+ const existingCampaign = matchingCampaign[0];
320
+ // Якщо addDefaultStreams === true, додаємо потоки до існуючої кампанії
321
+ if (addDefaultStreams) {
322
+ const originalStreams = await getStreamsByCampaignId(3175);
323
+ const existingStreams = await getStreamsByCampaignId(existingCampaign.id);
324
+ for (const stream of originalStreams) {
325
+ // Перевіряємо чи потік вже існує
326
+ const existingStream = existingStreams.find(s => s.name === stream.name);
327
+ if (!existingStream) {
328
+ await http_1.default.post('streams', {
329
+ name: stream.name,
330
+ campaign_id: existingCampaign.id,
331
+ schema: stream.schema,
332
+ type: stream.type,
333
+ action_type: stream.action_type,
334
+ weight: stream.weight,
335
+ offers: stream.offers.map((offer) => ({
336
+ offer_id: offer.offer_id,
337
+ share: offer.share,
338
+ state: offer.state
339
+ })),
340
+ filters: stream.filters.map((filter) => ({
341
+ name: filter.name,
342
+ mode: filter.mode,
343
+ payload: filter.payload
344
+ })),
345
+ position: stream.position,
346
+ state: stream.state
347
+ });
348
+ }
349
+ }
236
350
  }
351
+ return existingCampaign;
352
+ }
353
+ const originalCampaign = await getCampaignById(3175);
354
+ const maxPosition = Math.max(...allCampaigns.map(c => c.position || 0));
355
+ const originalStreams = await getStreamsByCampaignId(3175);
356
+ let allDomains = await exports.KeitaroService.getDomains(true);
357
+ if (!allDomains) {
358
+ throw Error(`Failed to get all domains list`);
359
+ }
360
+ const domain = allDomains[Math.floor(Math.random() * allDomains.length)];
361
+ const maxGroupId = Math.max(...allCampaigns.map(c => c.group_id || 0));
362
+ let alias = generateAlias();
363
+ let payload = {
364
+ // Унікальні поля
365
+ name: platformName ? platformCampaignName : name,
366
+ alias: alias,
367
+ domain_id: domain.id,
368
+ position: [maxPosition + 100],
369
+ group_id: maxGroupId + 1,
370
+ traffic_source_id: keitaro_utils_1.TRAFFIC_SOURCE_ID_FLASH_AI,
371
+ parameters: (0, keitaro_utils_1.prepareOWCampaignParameters)(app),
372
+ source: originalCampaign.source,
373
+ bind_visitors: "slo",
374
+ // Неунікальні поля з оригінальної кампанії
375
+ type: originalCampaign.type,
376
+ state: originalCampaign.state,
377
+ cost_type: originalCampaign.cost_type,
378
+ cost_value: originalCampaign.cost_value,
379
+ cost_currency: originalCampaign.cost_currency,
380
+ uniqueness_period: originalCampaign.uniqueness_period,
381
+ cookies_ttl: originalCampaign.cookies_ttl,
382
+ notes: originalCampaign.notes,
383
+ collect_clicks: originalCampaign.collect_clicks,
384
+ uniqueness_type: originalCampaign.uniqueness_type,
385
+ };
386
+ const newCampaign = await createCampaign(payload);
387
+ // Додаємо потоки: всі якщо addDefaultStreams === true, інакше тільки перший
388
+ const streamsToAdd = addDefaultStreams ? originalStreams : originalStreams.slice(0, 1);
389
+ for (const stream of streamsToAdd) {
390
+ await http_1.default.post('streams', {
391
+ name: stream.name,
392
+ campaign_id: newCampaign.id,
393
+ schema: stream.schema,
394
+ type: stream.type,
395
+ action_type: stream.action_type,
396
+ weight: stream.weight,
397
+ offers: stream.offers.map((offer) => ({
398
+ offer_id: offer.offer_id,
399
+ share: offer.share,
400
+ state: offer.state
401
+ })),
402
+ filters: stream.filters.map((filter) => ({
403
+ name: filter.name,
404
+ mode: filter.mode,
405
+ payload: filter.payload
406
+ })),
407
+ position: stream.position,
408
+ state: stream.state
409
+ });
410
+ }
411
+ await http_1.default.put(`/campaigns/${newCampaign.id}`, {
412
+ group_id: originalCampaign.group_id
413
+ });
414
+ const updatedCampaign = await getCampaignById(newCampaign.id);
415
+ return updatedCampaign;
416
+ }
417
+ async function cloneDCampaign(app) {
418
+ let name = `D #${app.id} (${app.bundle})`;
419
+ let allCampaigns = await getAllCampaigns();
420
+ let matchingCampaign = allCampaigns.filter((c) => c.name.includes(`D #${app.id}`));
421
+ if (matchingCampaign.length > 0)
422
+ return matchingCampaign[0];
423
+ const { data: campaigns } = await http_1.default.post(`/campaigns/2693/clone`);
424
+ if (campaigns.length == 0)
425
+ throw Error("Campaign cloning falied");
426
+ let clonedCampaign = campaigns[0];
427
+ let allDomains = await exports.KeitaroService.getDomains(true);
428
+ if (!allDomains) {
429
+ throw Error(`Failed to get all domains list`);
430
+ }
431
+ const domain = allDomains[Math.floor(Math.random() * allDomains.length)];
432
+ let payload = {
433
+ name: name,
434
+ traffic_source_id: keitaro_utils_1.TRAFFIC_SOURCE_ID_FLASH_AI,
435
+ domain_id: domain.id,
436
+ group_id: 93,
437
+ };
438
+ const { data: campaign } = await http_1.default.put(`/campaigns/${clonedCampaign.id}`, payload);
439
+ return campaign;
440
+ }
441
+ async function changeCampaignsGroup(fromId, toId, exceptForCampaignIds, onlyForCampaignIds) {
442
+ let campaigns = await getAllCampaigns();
443
+ const matchingCampaigns = campaigns.filter(campaign => campaign.group_id == fromId
444
+ && exceptForCampaignIds.length > 0 ? !exceptForCampaignIds.includes(campaign.id) : onlyForCampaignIds?.includes(campaign.id));
445
+ for (let campaign of matchingCampaigns) {
446
+ await updateCampaign(campaign.id, { group_id: toId });
447
+ }
448
+ }
449
+ // Domain
450
+ async function getDomains(onlyActive) {
451
+ const { data: domains } = await http_1.default.get(`/domains`);
452
+ return onlyActive ? domains.filter((d) => d.network_status == "active") : domains;
453
+ }
454
+ async function getProfitForTimeRange(from, to) {
455
+ let fromString = (0, general_1.convertMillisToDate)(from);
456
+ let toString = (0, general_1.convertMillisToDate)(to);
457
+ let { data: data } = await http_1.default.post(`/report/build`, {
458
+ range: {
459
+ from: fromString,
460
+ to: toString,
461
+ timezone: 'Europe/Kyiv'
462
+ },
463
+ metrics: ['profit']
464
+ });
465
+ return data.rows[0].profit;
466
+ }
467
+ // async function getProfitForTodayAndYesterday(): Promise<any> {
468
+ // let timestamps = getTimestampsForTodayAndYesterday()
469
+ // let today = await KeitaroService.getProfitForTimeRange(timestamps.today.start, timestamps.today.end)
470
+ // let yesterday = await KeitaroService.getProfitForTimeRange(timestamps.yesterday.start, timestamps.yesterday.end)
471
+ // return {
472
+ // today: today,
473
+ // yesterday: yesterday
474
+ // }
475
+ // }
476
+ async function fixBrokenClickCosts(startDate, endDate) {
477
+ // Get all campaigns to update
478
+ const campaigns = await getAllCampaigns();
479
+ const campaignIds = campaigns.map(campaign => campaign.id);
480
+ // Prepare the payload for the API request
481
+ const payload = {
482
+ campaign_ids: campaignIds,
483
+ costs: [{
484
+ start_date: startDate,
485
+ end_date: endDate,
486
+ cost: 0,
487
+ filters: {}
488
+ }],
489
+ timezone: "Europe/Kyiv",
490
+ currency: "USD",
491
+ only_campaign_uniques: 0
492
+ };
493
+ // Make the API request to update costs
494
+ await http_1.default.post('/clicks/update_costs', payload);
495
+ }
496
+ async function getClicks(request) {
497
+ const defaultRequest = typeof request === 'string' ? {
498
+ range: { interval: request }
499
+ } : {
500
+ ...request
237
501
  };
502
+ const { data } = await http_1.default.post('/clicks/log', defaultRequest);
503
+ return data.rows;
238
504
  }
239
505
  exports.KeitaroService = {
240
- // Campaign operations
241
- getAllCampaigns,
242
- getCampaignById,
243
- createCampaign,
244
- updateCampaign,
245
- cloneCampaignForApp,
246
- getOfferwallCampaigns,
247
- // Stream operations
248
- getStreamsByCampaignId,
249
- createStream,
250
- // Domain operations
251
- getDomains,
252
- // Offer operations
253
- getAllOffers,
254
- createOfferInKeitaro,
255
- addOfferToKeitaro,
256
- // Utilities
257
- generateAlias,
258
- campaignToKeitaroData,
506
+ getStreamsByCampaignId, updateCampaign, getAllCampaigns, getAllOffers, cloneStreams, addOffersToKeitaro, getOfferByKeitaroId, getDomains, createCampaign, getCampaignById, upsertStreamToCampaign, cloneOWCampaign,
507
+ updateOffer, changeCampaignsGroup, getProfitForTimeRange, getClicks,
508
+ // getProfitForTodayAndYesterday,
509
+ cloneDCampaign, findKeitaroOffers, fixBrokenClickCosts, cloneDirectCampaign
259
510
  };
260
- exports.default = exports.KeitaroService;