@bprotsyk/aso-core 2.1.195 → 2.1.196
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/app/app.d.ts +188 -181
- package/lib/app/app.js +60 -114
- package/lib/general/domain.d.ts +3 -3
- package/lib/index.d.ts +1 -7
- package/lib/index.js +6 -44
- package/lib/keitaro/keitaro-campaign.d.ts +1 -0
- package/lib/network/keitaro/http.js +1 -1
- package/lib/network/keitaro/keitaro-service.d.ts +25 -38
- package/lib/network/keitaro/keitaro-service.js +105 -450
- package/package.json +1 -1
- package/src/app/app.ts +96 -234
- package/src/index.ts +16 -10
- package/src/keitaro/keitaro-campaign.ts +1 -0
- package/src/network/keitaro/http.ts +1 -1
- package/src/network/keitaro/keitaro-service.ts +131 -545
- package/src/keitaro/keitaro-clo-geos.ts +0 -389
- package/src/network/keitaro/traffle/traffle-keitaro-service.ts +0 -1576
- package/src/panel/app/upsert-flash-app-request.ts +0 -36
- package/src/utils/keitaro-utils.ts +0 -853
- package/src/utils/traffle-keitaro-utils.ts +0 -45
|
@@ -3,11 +3,13 @@ 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 =
|
|
6
|
+
exports.KeitaroService = void 0;
|
|
7
7
|
const http_1 = __importDefault(require("./http"));
|
|
8
8
|
const app_1 = require("../../app/app");
|
|
9
|
-
|
|
10
|
-
const
|
|
9
|
+
// Default group IDs
|
|
10
|
+
const DEFAULT_OFFERS_GROUP_ID = 107;
|
|
11
|
+
const DEFAULT_CAMPAIGNS_GROUP_ID = 106;
|
|
12
|
+
// Basic campaign operations
|
|
11
13
|
async function getStreamsByCampaignId(campaignId) {
|
|
12
14
|
const { data: streams } = await http_1.default.get(`campaigns/${campaignId}/streams`);
|
|
13
15
|
return streams;
|
|
@@ -16,377 +18,84 @@ async function getAllCampaigns() {
|
|
|
16
18
|
const { data: campaigns } = await http_1.default.get('campaigns');
|
|
17
19
|
return campaigns;
|
|
18
20
|
}
|
|
19
|
-
async function
|
|
20
|
-
|
|
21
|
-
|
|
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
|
-
}
|
|
21
|
+
async function getCampaignById(id) {
|
|
22
|
+
const { data: campaign } = await http_1.default.get(`/campaigns/${id}`);
|
|
23
|
+
return campaign;
|
|
55
24
|
}
|
|
56
25
|
async function updateCampaign(id, payload) {
|
|
57
26
|
await http_1.default.put(`campaigns/${id}`, payload);
|
|
58
27
|
}
|
|
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
|
-
}
|
|
94
|
-
}
|
|
95
|
-
async function getAllOffers() {
|
|
96
|
-
const { data: offers } = await http_1.default.get('offers');
|
|
97
|
-
return offers;
|
|
98
|
-
}
|
|
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;
|
|
108
|
-
}
|
|
109
|
-
async function getOfferByKeitaroId(id) {
|
|
110
|
-
const { data: offer } = await http_1.default.get(`offers/${id}`);
|
|
111
|
-
return offer;
|
|
112
|
-
}
|
|
113
|
-
async function updateOffer(offer) {
|
|
114
|
-
const { data: o } = await http_1.default.put(`offers/${offer.id}`, offer);
|
|
115
|
-
return o;
|
|
116
|
-
}
|
|
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;
|
|
146
|
-
}
|
|
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;
|
|
161
|
-
}
|
|
162
|
-
streamPayloads.push({
|
|
163
|
-
payload: createStreamPartialPayload(keitaroOfferId, offer.caption, offer.name, offer.geo),
|
|
164
|
-
offerId: offer.name
|
|
165
|
-
});
|
|
166
|
-
}
|
|
167
|
-
await createStreamForMatchingCampaigns(streamPayloads, /◈/, avoidGroup);
|
|
168
|
-
}
|
|
169
28
|
async function createCampaign(campaignData) {
|
|
170
29
|
let { data: campaign } = await http_1.default.post(`/campaigns`, campaignData);
|
|
171
30
|
return campaign;
|
|
172
31
|
}
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
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;
|
|
176
36
|
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
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
|
-
});
|
|
186
|
-
}
|
|
187
|
-
else {
|
|
188
|
-
await http_1.default.post('streams', {
|
|
189
|
-
campaign_id: campaign.id,
|
|
190
|
-
...stream
|
|
191
|
-
});
|
|
192
|
-
}
|
|
37
|
+
// Offer operations
|
|
38
|
+
async function getAllOffers() {
|
|
39
|
+
const { data: offers } = await http_1.default.get('offers');
|
|
40
|
+
return offers;
|
|
193
41
|
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
const
|
|
220
|
-
|
|
221
|
-
const
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
throw Error(`Failed to get all domains list`);
|
|
42
|
+
// Generate random alias for campaigns
|
|
43
|
+
function generateAlias() {
|
|
44
|
+
const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
|
45
|
+
return Array.from({ length: 8 }, () => chars.charAt(Math.floor(Math.random() * chars.length))).join('');
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Clone a campaign for an app based on integration type
|
|
49
|
+
* @param app - The app to create campaign for
|
|
50
|
+
* @param integrationType - OFFERWALL or DIRECT
|
|
51
|
+
* @returns Keitaro data for the cloned campaign
|
|
52
|
+
*/
|
|
53
|
+
async function cloneCampaignForApp(app, integrationType) {
|
|
54
|
+
const templateId = integrationType === app_1.IntegrationType.OFFERWALL
|
|
55
|
+
? app_1.OFFERWALL_TEMPLATE_CAMPAIGN_ID
|
|
56
|
+
: app_1.DIRECT_TEMPLATE_CAMPAIGN_ID;
|
|
57
|
+
const campaignPrefix = integrationType === app_1.IntegrationType.OFFERWALL ? '[◈]' : '[➥]';
|
|
58
|
+
const campaignName = `#${app.id} ${campaignPrefix} ${app.name || app.bundle}`;
|
|
59
|
+
// Check if campaign already exists
|
|
60
|
+
const allCampaigns = await getAllCampaigns();
|
|
61
|
+
const existingCampaign = allCampaigns.find(c => c.name.includes(`#${app.id}`) && c.name.includes(campaignPrefix));
|
|
62
|
+
if (existingCampaign) {
|
|
63
|
+
return campaignToKeitaroData(existingCampaign);
|
|
64
|
+
}
|
|
65
|
+
// Get template campaign and its streams
|
|
66
|
+
const templateCampaign = await getCampaignById(templateId);
|
|
67
|
+
const templateStreams = await getStreamsByCampaignId(templateId);
|
|
68
|
+
// Get random active domain
|
|
69
|
+
const allDomains = await getDomains(true);
|
|
70
|
+
if (!allDomains || allDomains.length === 0) {
|
|
71
|
+
throw new Error('No active domains available');
|
|
225
72
|
}
|
|
226
73
|
const domain = allDomains[Math.floor(Math.random() * allDomains.length)];
|
|
227
|
-
|
|
228
|
-
let alias = generateAlias();
|
|
229
|
-
let payload = {
|
|
230
|
-
// Унікальні поля
|
|
231
|
-
name: platformName ? platformCampaignName : name,
|
|
232
|
-
alias: alias,
|
|
233
|
-
domain_id: domain.id,
|
|
234
|
-
position: [maxPosition + 100],
|
|
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,
|
|
249
|
-
};
|
|
250
|
-
const newCampaign = await createCampaign(payload);
|
|
251
|
-
for (const stream of originalStreams) {
|
|
252
|
-
await http_1.default.post('streams', {
|
|
253
|
-
name: stream.name,
|
|
254
|
-
campaign_id: newCampaign.id,
|
|
255
|
-
schema: stream.schema,
|
|
256
|
-
type: stream.type,
|
|
257
|
-
action_type: stream.action_type,
|
|
258
|
-
weight: stream.weight,
|
|
259
|
-
offers: stream.offers.map((offer) => ({
|
|
260
|
-
offer_id: offer.offer_id,
|
|
261
|
-
share: offer.share,
|
|
262
|
-
state: offer.state
|
|
263
|
-
})),
|
|
264
|
-
filters: stream.filters.map((filter) => ({
|
|
265
|
-
name: filter.name,
|
|
266
|
-
mode: filter.mode,
|
|
267
|
-
payload: filter.payload
|
|
268
|
-
})),
|
|
269
|
-
position: stream.position,
|
|
270
|
-
state: stream.state
|
|
271
|
-
});
|
|
272
|
-
}
|
|
273
|
-
await http_1.default.put(`/campaigns/${newCampaign.id}`, {
|
|
274
|
-
group_id: originalCampaign.group_id
|
|
275
|
-
});
|
|
276
|
-
const updatedCampaign = await getCampaignById(newCampaign.id);
|
|
277
|
-
return updatedCampaign;
|
|
278
|
-
}
|
|
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
|
-
}
|
|
350
|
-
}
|
|
351
|
-
return existingCampaign;
|
|
352
|
-
}
|
|
353
|
-
const originalCampaign = await getCampaignById(3175);
|
|
74
|
+
// Create campaign payload
|
|
354
75
|
const maxPosition = Math.max(...allCampaigns.map(c => c.position || 0));
|
|
355
|
-
const
|
|
356
|
-
|
|
357
|
-
|
|
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,
|
|
76
|
+
const alias = generateAlias();
|
|
77
|
+
const payload = {
|
|
78
|
+
name: campaignName,
|
|
366
79
|
alias: alias,
|
|
367
80
|
domain_id: domain.id,
|
|
368
81
|
position: [maxPosition + 100],
|
|
369
|
-
group_id:
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
cookies_ttl: originalCampaign.cookies_ttl,
|
|
382
|
-
notes: originalCampaign.notes,
|
|
383
|
-
collect_clicks: originalCampaign.collect_clicks,
|
|
384
|
-
uniqueness_type: originalCampaign.uniqueness_type,
|
|
82
|
+
group_id: DEFAULT_CAMPAIGNS_GROUP_ID,
|
|
83
|
+
type: templateCampaign.type,
|
|
84
|
+
state: templateCampaign.state,
|
|
85
|
+
cost_type: templateCampaign.cost_type,
|
|
86
|
+
cost_value: templateCampaign.cost_value,
|
|
87
|
+
cost_currency: templateCampaign.cost_currency,
|
|
88
|
+
uniqueness_period: templateCampaign.uniqueness_period,
|
|
89
|
+
cookies_ttl: templateCampaign.cookies_ttl,
|
|
90
|
+
notes: templateCampaign.notes,
|
|
91
|
+
collect_clicks: templateCampaign.collect_clicks,
|
|
92
|
+
uniqueness_type: templateCampaign.uniqueness_type,
|
|
93
|
+
parameters: templateCampaign.parameters,
|
|
385
94
|
};
|
|
95
|
+
// Create the new campaign
|
|
386
96
|
const newCampaign = await createCampaign(payload);
|
|
387
|
-
//
|
|
388
|
-
const
|
|
389
|
-
for (const stream of streamsToAdd) {
|
|
97
|
+
// Clone streams from template
|
|
98
|
+
for (const stream of templateStreams) {
|
|
390
99
|
await http_1.default.post('streams', {
|
|
391
100
|
name: stream.name,
|
|
392
101
|
campaign_id: newCampaign.id,
|
|
@@ -408,103 +117,49 @@ async function cloneDirectCampaign(app, platform, addDefaultStreams) {
|
|
|
408
117
|
state: stream.state
|
|
409
118
|
});
|
|
410
119
|
}
|
|
411
|
-
|
|
412
|
-
group_id: originalCampaign.group_id
|
|
413
|
-
});
|
|
120
|
+
// Get updated campaign with token
|
|
414
121
|
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;
|
|
122
|
+
return campaignToKeitaroData(updatedCampaign);
|
|
440
123
|
}
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
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
|
|
124
|
+
/**
|
|
125
|
+
* Convert a Keitaro campaign to IAppKeitaroData
|
|
126
|
+
*/
|
|
127
|
+
function campaignToKeitaroData(campaign) {
|
|
128
|
+
return {
|
|
129
|
+
trackingCampaignId: campaign.id,
|
|
130
|
+
trackingCampaignName: campaign.name,
|
|
131
|
+
trackingCampaignAlias: campaign.alias,
|
|
132
|
+
trackingDomainId: campaign.domain_id,
|
|
133
|
+
trackingDomainName: campaign.domain || '',
|
|
134
|
+
campingToken: campaign.token,
|
|
135
|
+
trackingParams: {
|
|
136
|
+
naming: campaign.parameters?.sub_id_15?.name || 'naming',
|
|
137
|
+
firebase_app_instance_id: campaign.parameters?.sub_id_11?.name || 'firebase_app_instance_id',
|
|
138
|
+
firebase_user_id: campaign.parameters?.sub_id_12?.name || 'firebase_user_id',
|
|
139
|
+
firebase_device_language_code: campaign.parameters?.sub_id_13?.name || 'firebase_device_language_code',
|
|
140
|
+
firebase_push_token: campaign.parameters?.sub_id_14?.name || 'firebase_push_token',
|
|
141
|
+
bundle_id: campaign.parameters?.sub_id_16?.name || 'bundle_id',
|
|
142
|
+
advertising_id: campaign.parameters?.sub_id_17?.name || 'advertising_id',
|
|
143
|
+
appsflyer_device_id: campaign.parameters?.sub_id_18?.name || 'appsflyer_device_id',
|
|
144
|
+
campaign: campaign.parameters?.sub_id_20?.name || 'campaign',
|
|
145
|
+
}
|
|
501
146
|
};
|
|
502
|
-
const { data } = await http_1.default.post('/clicks/log', defaultRequest);
|
|
503
|
-
return data.rows;
|
|
504
147
|
}
|
|
505
148
|
exports.KeitaroService = {
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
149
|
+
// Campaign operations
|
|
150
|
+
getAllCampaigns,
|
|
151
|
+
getCampaignById,
|
|
152
|
+
createCampaign,
|
|
153
|
+
updateCampaign,
|
|
154
|
+
cloneCampaignForApp,
|
|
155
|
+
// Stream operations
|
|
156
|
+
getStreamsByCampaignId,
|
|
157
|
+
// Domain operations
|
|
158
|
+
getDomains,
|
|
159
|
+
// Offer operations
|
|
160
|
+
getAllOffers,
|
|
161
|
+
// Utilities
|
|
162
|
+
generateAlias,
|
|
163
|
+
campaignToKeitaroData,
|
|
510
164
|
};
|
|
165
|
+
exports.default = exports.KeitaroService;
|