@bprotsyk/aso-core 2.1.142 → 2.1.181
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 +75 -0
- package/lib/app/app.js +28 -1
- package/lib/index.d.ts +1 -1
- package/lib/keitaro/keitaro-conversions.d.ts +52 -0
- package/lib/keitaro/keitaro-conversions.js +27 -0
- package/lib/keitaro/keitaro-utils.d.ts +2 -0
- package/lib/keitaro/keitaro-utils.js +108 -0
- package/lib/keitaro-utils.d.ts +2 -0
- package/lib/keitaro-utils.js +136 -0
- package/lib/network/keitaro/traffle/traffle-keitaro-service.d.ts +7 -0
- package/lib/network/keitaro/traffle/traffle-keitaro-service.js +234 -2
- package/lib/utils/comparing.d.ts +31 -0
- package/lib/utils/comparing.js +104 -0
- package/package.json +1 -1
- package/src/app/app.ts +38 -1
- package/src/index.ts +1 -1
- package/src/network/keitaro/traffle/traffle-keitaro-service.ts +272 -1
- package/lib/templates/words.txt +0 -212
|
@@ -3,7 +3,7 @@ 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.TraffleKeitaroService = exports.updateStreamsForApp = exports.ITraffleTrafficType = void 0;
|
|
6
|
+
exports.TraffleKeitaroService = exports.updateCampaignNamesToNewPattern = exports.updateStreamsForApp = exports.ITraffleTrafficType = void 0;
|
|
7
7
|
const http_1 = __importDefault(require("./http"));
|
|
8
8
|
const app_1 = require("../../../app/app");
|
|
9
9
|
const openai_1 = require("../../../network/openai/openai");
|
|
@@ -290,6 +290,24 @@ async function checkAndUpdateCampaignParameters(existingCampaign, app, platform)
|
|
|
290
290
|
return await updateCampaignWithAppParams(existingCampaign, app, platform);
|
|
291
291
|
}
|
|
292
292
|
console.log("Parameters are up to date, no update needed");
|
|
293
|
+
// Always add postback even if no parameter update is needed
|
|
294
|
+
try {
|
|
295
|
+
console.log(`[Traffle] Adding postback to campaign ${existingCampaign.id} (no parameter update needed)`);
|
|
296
|
+
const postbackUrl = `https://traffle-tech.com/lander/appsflyer-s2s/index.php?status={status}&bundle_id={sub_id_16}&advertising_id={sub_id_17}&appsflyer_device_id={sub_id_18}&appsflyer_dev_key={sub_id_19}`;
|
|
297
|
+
await http_1.default.put(`/campaigns/${existingCampaign.id}`, {
|
|
298
|
+
postbacks: [{
|
|
299
|
+
id: 0,
|
|
300
|
+
campaign_id: existingCampaign.id,
|
|
301
|
+
method: "GET",
|
|
302
|
+
statuses: ["lead", "sale", "rejected", "rebill"],
|
|
303
|
+
url: postbackUrl
|
|
304
|
+
}]
|
|
305
|
+
});
|
|
306
|
+
console.log(`[Traffle] Postback added to campaign ${existingCampaign.id}`);
|
|
307
|
+
}
|
|
308
|
+
catch (error) {
|
|
309
|
+
console.error(`[Traffle] Failed to add postback to campaign ${existingCampaign.id}:`, error);
|
|
310
|
+
}
|
|
293
311
|
return false;
|
|
294
312
|
}
|
|
295
313
|
return false;
|
|
@@ -465,6 +483,7 @@ async function updateAppWithCampaignParams(existingCampaign, app, platform) {
|
|
|
465
483
|
}
|
|
466
484
|
}
|
|
467
485
|
async function cloneTraffleCampaign(app, platform, addDefaultStreams) {
|
|
486
|
+
console.log(`[Traffle] cloneTraffleCampaign called for app ${app.id}, platform ${platform}, addDefaultStreams: ${addDefaultStreams}`);
|
|
468
487
|
const ORIGINAL_CLONE_CAMPAIGN_ID = 1925;
|
|
469
488
|
const appsflyerAvailability = app.platforms[platform].appsflyerParams?.apiToken ? "[AF]" : "";
|
|
470
489
|
let name = `[${app.id}] [${app.bundle}] ${app.name} [Volodymyr Vinnik] [El Trafico] [Android] ${appsflyerAvailability} ${app.domainParams.name}`;
|
|
@@ -593,6 +612,24 @@ async function cloneTraffleCampaign(app, platform, addDefaultStreams) {
|
|
|
593
612
|
}
|
|
594
613
|
}
|
|
595
614
|
}
|
|
615
|
+
// Add postback to existing campaign
|
|
616
|
+
try {
|
|
617
|
+
console.log(`[Traffle] Adding postback to existing campaign ${existingCampaign.id}`);
|
|
618
|
+
const postbackUrl = `https://traffle-tech.com/lander/appsflyer-s2s/index.php?status={status}&bundle_id={sub_id_16}&advertising_id={sub_id_17}&appsflyer_device_id={sub_id_18}&appsflyer_dev_key={sub_id_19}`;
|
|
619
|
+
await http_1.default.put(`/campaigns/${existingCampaign.id}`, {
|
|
620
|
+
postbacks: [{
|
|
621
|
+
id: 0,
|
|
622
|
+
campaign_id: existingCampaign.id,
|
|
623
|
+
method: "GET",
|
|
624
|
+
statuses: ["lead", "sale", "rejected", "rebill"],
|
|
625
|
+
url: postbackUrl
|
|
626
|
+
}]
|
|
627
|
+
});
|
|
628
|
+
console.log(`[Traffle] Postback added to campaign ${existingCampaign.id}`);
|
|
629
|
+
}
|
|
630
|
+
catch (error) {
|
|
631
|
+
console.error(`[Traffle] Failed to add postback to campaign ${existingCampaign.id}:`, error);
|
|
632
|
+
}
|
|
596
633
|
console.log(existingCampaign);
|
|
597
634
|
return existingCampaign;
|
|
598
635
|
}
|
|
@@ -720,6 +757,24 @@ async function cloneTraffleCampaign(app, platform, addDefaultStreams) {
|
|
|
720
757
|
await createStreamWithoutIds(stream, newCampaign.id);
|
|
721
758
|
}
|
|
722
759
|
const updatedCampaign = await getCampaignById(newCampaign.id);
|
|
760
|
+
// Add postback to new campaign
|
|
761
|
+
try {
|
|
762
|
+
console.log(`[Traffle] Adding postback to new campaign ${updatedCampaign.id}`);
|
|
763
|
+
const postbackUrl = `https://traffle-tech.com/lander/appsflyer-s2s/index.php?status={status}&bundle_id={sub_id_16}&advertising_id={sub_id_17}&appsflyer_device_id={sub_id_18}&appsflyer_dev_key={sub_id_19}`;
|
|
764
|
+
await http_1.default.put(`/campaigns/${updatedCampaign.id}`, {
|
|
765
|
+
postbacks: [{
|
|
766
|
+
id: 0,
|
|
767
|
+
campaign_id: updatedCampaign.id,
|
|
768
|
+
method: "GET",
|
|
769
|
+
statuses: ["lead", "sale", "rejected", "rebill"],
|
|
770
|
+
url: postbackUrl
|
|
771
|
+
}]
|
|
772
|
+
});
|
|
773
|
+
console.log(`[Traffle] Postback added to campaign ${updatedCampaign.id}`);
|
|
774
|
+
}
|
|
775
|
+
catch (error) {
|
|
776
|
+
console.error(`[Traffle] Failed to add postback to campaign ${updatedCampaign.id}:`, error);
|
|
777
|
+
}
|
|
723
778
|
return updatedCampaign;
|
|
724
779
|
}
|
|
725
780
|
async function findCampaignForAppPlatform(app, platform) {
|
|
@@ -840,6 +895,183 @@ async function updateStreamsForApp(app, platform) {
|
|
|
840
895
|
}
|
|
841
896
|
}
|
|
842
897
|
exports.updateStreamsForApp = updateStreamsForApp;
|
|
898
|
+
async function findCampaignsWithOldNamingPattern() {
|
|
899
|
+
try {
|
|
900
|
+
const allCampaigns = await getAllCampaigns();
|
|
901
|
+
// Pattern: [id] app_name [bundle] [platform] domain.com (flexible - ID is optional)
|
|
902
|
+
// We want to find campaigns that DON'T have [Volodymyr Vinnik] in them
|
|
903
|
+
const oldPatternCampaigns = allCampaigns.filter(campaign => {
|
|
904
|
+
const name = campaign.name;
|
|
905
|
+
// Check if it matches the old pattern: BAN_prefix [id] app_name [bundle] [platform] domain (flexible)
|
|
906
|
+
const oldPattern = /^(BAN[_\w.]*\s+)?(\[(\d+)\]\s+)?([^[]+)\s+\[([^\]]+)\]\s+\[([^\]]+)\]\s+(.+)$/;
|
|
907
|
+
const match = name.match(oldPattern);
|
|
908
|
+
if (!match)
|
|
909
|
+
return false;
|
|
910
|
+
// Check if it doesn't already have [Volodymyr Vinnik]
|
|
911
|
+
return !name.includes('[Volodymyr Vinnik]');
|
|
912
|
+
});
|
|
913
|
+
console.log(`Found ${oldPatternCampaigns.length} campaigns with old naming pattern`);
|
|
914
|
+
return oldPatternCampaigns;
|
|
915
|
+
}
|
|
916
|
+
catch (error) {
|
|
917
|
+
console.error("Error finding campaigns with old naming pattern:", error);
|
|
918
|
+
return [];
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
async function updateCampaignNameToNewPattern(campaign, requireConfirmation = false) {
|
|
922
|
+
try {
|
|
923
|
+
const name = campaign.name;
|
|
924
|
+
// Parse the old pattern: BAN_prefix [id] app_name [bundle] [platform] domain (flexible - BAN and ID are optional)
|
|
925
|
+
const oldPattern = /^(BAN[_\w.]*\s+)?(\[(\d+)\]\s+)?([^[]+)\s+\[([^\]]+)\]\s+\[([^\]]+)\]\s+(.+)$/;
|
|
926
|
+
const match = name.match(oldPattern);
|
|
927
|
+
if (!match) {
|
|
928
|
+
console.log(`Campaign ${campaign.id} doesn't match old pattern: ${name}`);
|
|
929
|
+
return false;
|
|
930
|
+
}
|
|
931
|
+
const [, banPrefix, , idFromName, appName, bundle, platform, domain] = match;
|
|
932
|
+
// Create new name based on what exists in original name
|
|
933
|
+
let newName;
|
|
934
|
+
if (banPrefix && idFromName) {
|
|
935
|
+
// Original had BAN and ID: BAN_prefix [id] [bundle] app_name [Volodymyr Vinnik] [El Trafico] [platform] domain
|
|
936
|
+
newName = `${banPrefix.trim()} [${idFromName}] [${bundle}] ${appName.trim()} [Volodymyr Vinnik] [El Trafico] [${platform}] ${domain}`;
|
|
937
|
+
}
|
|
938
|
+
else if (banPrefix && !idFromName) {
|
|
939
|
+
// Original had BAN but no ID: BAN_prefix [bundle] app_name [Volodymyr Vinnik] [El Trafico] [platform] domain
|
|
940
|
+
newName = `${banPrefix.trim()} [${bundle}] ${appName.trim()} [Volodymyr Vinnik] [El Trafico] [${platform}] ${domain}`;
|
|
941
|
+
}
|
|
942
|
+
else if (!banPrefix && idFromName) {
|
|
943
|
+
// Original had ID but no BAN: [id] [bundle] app_name [Volodymyr Vinnik] [El Trafico] [platform] domain
|
|
944
|
+
newName = `[${idFromName}] [${bundle}] ${appName.trim()} [Volodymyr Vinnik] [El Trafico] [${platform}] ${domain}`;
|
|
945
|
+
}
|
|
946
|
+
else {
|
|
947
|
+
// Original had neither BAN nor ID: [bundle] app_name [Volodymyr Vinnik] [El Trafico] [platform] domain
|
|
948
|
+
newName = `[${bundle}] ${appName.trim()} [Volodymyr Vinnik] [El Trafico] [${platform}] ${domain}`;
|
|
949
|
+
}
|
|
950
|
+
if (requireConfirmation) {
|
|
951
|
+
console.log(`\nCampaign ${campaign.id}:`);
|
|
952
|
+
console.log(`OLD: "${name}"`);
|
|
953
|
+
console.log(`NEW: "${newName}"`);
|
|
954
|
+
console.log(`\nDo you want to update this campaign? (y/n/s to skip all remaining):`);
|
|
955
|
+
const confirmed = await confirmUpdate();
|
|
956
|
+
if (!confirmed) {
|
|
957
|
+
console.log(`Skipped campaign ${campaign.id}`);
|
|
958
|
+
return false;
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
// Update the campaign
|
|
962
|
+
await http_1.default.put(`/campaigns/${campaign.id}`, { name: newName });
|
|
963
|
+
console.log(`Updated campaign ${campaign.id}: "${name}" → "${newName}"`);
|
|
964
|
+
return true;
|
|
965
|
+
}
|
|
966
|
+
catch (error) {
|
|
967
|
+
console.error(`Failed to update campaign ${campaign.id}:`, error);
|
|
968
|
+
return false;
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
// Helper function to handle user confirmation
|
|
972
|
+
async function confirmUpdate() {
|
|
973
|
+
const readline = require('readline');
|
|
974
|
+
const rl = readline.createInterface({
|
|
975
|
+
input: process.stdin,
|
|
976
|
+
output: process.stdout
|
|
977
|
+
});
|
|
978
|
+
return new Promise((resolve) => {
|
|
979
|
+
rl.question('', (answer) => {
|
|
980
|
+
rl.close();
|
|
981
|
+
const lowerAnswer = answer.toLowerCase().trim();
|
|
982
|
+
if (lowerAnswer === 's') {
|
|
983
|
+
// Skip all remaining
|
|
984
|
+
console.log("Skipping all remaining updates...");
|
|
985
|
+
process.exit(0);
|
|
986
|
+
}
|
|
987
|
+
resolve(lowerAnswer === 'y' || lowerAnswer === 'yes');
|
|
988
|
+
});
|
|
989
|
+
});
|
|
990
|
+
}
|
|
991
|
+
async function updateCampaignNamesToNewPattern(requireConfirmation = false) {
|
|
992
|
+
try {
|
|
993
|
+
console.log("Starting campaign name update process...");
|
|
994
|
+
const oldPatternCampaigns = await findCampaignsWithOldNamingPattern();
|
|
995
|
+
if (oldPatternCampaigns.length === 0) {
|
|
996
|
+
console.log("No campaigns found with old naming pattern");
|
|
997
|
+
return { updated: 0, failed: 0 };
|
|
998
|
+
}
|
|
999
|
+
console.log(`Found ${oldPatternCampaigns.length} campaigns to potentially update`);
|
|
1000
|
+
if (requireConfirmation) {
|
|
1001
|
+
console.log("Interactive mode: You will be asked to confirm each update");
|
|
1002
|
+
}
|
|
1003
|
+
let updated = 0;
|
|
1004
|
+
let failed = 0;
|
|
1005
|
+
for (const campaign of oldPatternCampaigns) {
|
|
1006
|
+
const success = await updateCampaignNameToNewPattern(campaign, requireConfirmation);
|
|
1007
|
+
if (success) {
|
|
1008
|
+
updated++;
|
|
1009
|
+
}
|
|
1010
|
+
else {
|
|
1011
|
+
failed++;
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
console.log(`Campaign name update completed: ${updated} updated, ${failed} failed`);
|
|
1015
|
+
return { updated, failed };
|
|
1016
|
+
}
|
|
1017
|
+
catch (error) {
|
|
1018
|
+
console.error("Failed to update campaign names:", error);
|
|
1019
|
+
return { updated: 0, failed: 0 };
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
exports.updateCampaignNamesToNewPattern = updateCampaignNamesToNewPattern;
|
|
1023
|
+
async function ensurePostbacksForCampaign(campaignId, appId, bundle) {
|
|
1024
|
+
try {
|
|
1025
|
+
console.log(`[Traffle] Starting ensurePostbacksForCampaign for campaign ${campaignId}, app ${appId}, bundle ${bundle}`);
|
|
1026
|
+
const postbackUrl = `https://traffle-tech.com/lander/appsflyer-s2s/index.php?status={status}&bundle_id={sub_id_16}&advertising_id={sub_id_17}&appsflyer_device_id={sub_id_18}&appsflyer_dev_key={sub_id_19}`;
|
|
1027
|
+
// Check if postback already exists
|
|
1028
|
+
console.log(`[Traffle] Getting existing postbacks for campaign ${campaignId}`);
|
|
1029
|
+
const existingPostbacks = await getCampaignPostbacks(campaignId);
|
|
1030
|
+
console.log(`[Traffle] Existing postbacks:`, existingPostbacks);
|
|
1031
|
+
const postbackExists = existingPostbacks?.some(postback => postback.url === postbackUrl &&
|
|
1032
|
+
postback.method === "GET" &&
|
|
1033
|
+
postback.statuses?.includes("lead"));
|
|
1034
|
+
if (postbackExists) {
|
|
1035
|
+
console.log(`[Traffle] AppsFlyer postback already exists for Traffle campaign ${campaignId}, skipping`);
|
|
1036
|
+
return;
|
|
1037
|
+
}
|
|
1038
|
+
const postbackData = {
|
|
1039
|
+
id: 0,
|
|
1040
|
+
campaign_id: campaignId,
|
|
1041
|
+
method: "GET",
|
|
1042
|
+
statuses: ["lead", "sale", "rejected", "rebill"],
|
|
1043
|
+
url: postbackUrl
|
|
1044
|
+
};
|
|
1045
|
+
console.log(`[Traffle] Adding AppsFlyer postback to Traffle campaign ${campaignId}:`, postbackData);
|
|
1046
|
+
// Use Traffle API to add postback via campaign update
|
|
1047
|
+
console.log(`[Traffle] Making PUT request to /campaigns/${campaignId} with postbacks:`, [postbackData]);
|
|
1048
|
+
const response = await http_1.default.put(`/campaigns/${campaignId}`, {
|
|
1049
|
+
postbacks: [postbackData]
|
|
1050
|
+
});
|
|
1051
|
+
console.log(`[Traffle] Postback creation response:`, response.status, response.data?.postbacks);
|
|
1052
|
+
if (response.status === 200) {
|
|
1053
|
+
console.log(`[Traffle] Successfully added AppsFlyer postback to Traffle campaign ${campaignId}`);
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
catch (error) {
|
|
1057
|
+
console.error(`Failed to add AppsFlyer postback to Traffle campaign ${campaignId}:`, error);
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
async function getCampaignPostbacks(campaignId) {
|
|
1061
|
+
try {
|
|
1062
|
+
console.log(`[Traffle] Getting postbacks for campaign ${campaignId}`);
|
|
1063
|
+
const response = await http_1.default.get(`/campaigns/${campaignId}`);
|
|
1064
|
+
console.log(`[Traffle] Campaign ${campaignId} response:`, response.status, response.data?.postbacks);
|
|
1065
|
+
if (response.status === 200 && response.data) {
|
|
1066
|
+
return response.data.postbacks || [];
|
|
1067
|
+
}
|
|
1068
|
+
return null;
|
|
1069
|
+
}
|
|
1070
|
+
catch (error) {
|
|
1071
|
+
console.error(`[Traffle] Failed to get postbacks for Traffle campaign ${campaignId}:`, error);
|
|
1072
|
+
return null;
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
843
1075
|
exports.TraffleKeitaroService = {
|
|
844
|
-
addOffersToTraffleKeitaro, getTraffleOffersGroups, getTraffleAffiliateNetworks, createGroup, deleteOfferById, getAllOffers, updateOfferLinkById, cloneTraffleCampaign, TrafleKeitaroParameters, updateStreamsForApp
|
|
1076
|
+
addOffersToTraffleKeitaro, getTraffleOffersGroups, getTraffleAffiliateNetworks, createGroup, deleteOfferById, getAllOffers, getAllCampaigns, updateOfferLinkById, cloneTraffleCampaign, TrafleKeitaroParameters, updateStreamsForApp, updateCampaignNamesToNewPattern
|
|
845
1077
|
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export interface WPConversion {
|
|
2
|
+
status: string;
|
|
3
|
+
regDate: Date;
|
|
4
|
+
qualDate: Date;
|
|
5
|
+
approveOrRejectDate: Date;
|
|
6
|
+
project: string;
|
|
7
|
+
offerName: string;
|
|
8
|
+
sum: number;
|
|
9
|
+
currency: string;
|
|
10
|
+
geo: string;
|
|
11
|
+
subId?: string;
|
|
12
|
+
clickId: string;
|
|
13
|
+
}
|
|
14
|
+
export interface ConversionComparison {
|
|
15
|
+
wpConversions: WPConversion[];
|
|
16
|
+
keitaroConversions: any[];
|
|
17
|
+
matches: {
|
|
18
|
+
wp: WPConversion;
|
|
19
|
+
keitaro: any;
|
|
20
|
+
}[];
|
|
21
|
+
mismatches: {
|
|
22
|
+
onlyInWP: WPConversion[];
|
|
23
|
+
onlyInKeitaro: any[];
|
|
24
|
+
};
|
|
25
|
+
rejected: {
|
|
26
|
+
wp: WPConversion[];
|
|
27
|
+
keitaro: any[];
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export declare function parseWPConversions(filePath: string): Promise<WPConversion[]>;
|
|
31
|
+
export declare function compareConversions(wpFilePath: string, keitaroConversions: any[]): Promise<ConversionComparison>;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.compareConversions = exports.parseWPConversions = void 0;
|
|
27
|
+
const sync_1 = require("csv-parse/sync");
|
|
28
|
+
const fs = __importStar(require("fs"));
|
|
29
|
+
function parseDate(dateStr) {
|
|
30
|
+
const [month, day, year] = dateStr.split('/').map(num => parseInt(num));
|
|
31
|
+
return new Date(2000 + year, month - 1, day);
|
|
32
|
+
}
|
|
33
|
+
async function parseWPConversions(filePath) {
|
|
34
|
+
const fileContent = fs.readFileSync(filePath, 'utf-8');
|
|
35
|
+
const records = (0, sync_1.parse)(fileContent, {
|
|
36
|
+
delimiter: ';',
|
|
37
|
+
columns: true,
|
|
38
|
+
skip_empty_lines: true
|
|
39
|
+
});
|
|
40
|
+
return records.map((record) => ({
|
|
41
|
+
status: record.Status,
|
|
42
|
+
regDate: parseDate(record.RegDate),
|
|
43
|
+
qualDate: parseDate(record.QualDate),
|
|
44
|
+
approveOrRejectDate: parseDate(record.Approve_or_Reject_Date),
|
|
45
|
+
project: record.Project,
|
|
46
|
+
offerName: record.Offer_Name,
|
|
47
|
+
sum: parseFloat(record.Sum),
|
|
48
|
+
currency: record.Currency,
|
|
49
|
+
geo: record.Geo,
|
|
50
|
+
subId: record.Sub_ID || undefined,
|
|
51
|
+
clickId: record.Click_ID
|
|
52
|
+
}));
|
|
53
|
+
}
|
|
54
|
+
exports.parseWPConversions = parseWPConversions;
|
|
55
|
+
async function compareConversions(wpFilePath, keitaroConversions) {
|
|
56
|
+
const wpConversions = await parseWPConversions(wpFilePath);
|
|
57
|
+
const matches = [];
|
|
58
|
+
const onlyInWP = [];
|
|
59
|
+
const onlyInKeitaro = [];
|
|
60
|
+
const rejectedWP = [];
|
|
61
|
+
const rejectedKeitaro = [];
|
|
62
|
+
// Create a map of Keitaro conversions by their sub_id to match with WP's Click_ID
|
|
63
|
+
const keitaroMap = new Map(keitaroConversions.map(conv => [conv.sub_id, conv]));
|
|
64
|
+
// First, find matches and conversions only in WP
|
|
65
|
+
for (const wpConv of wpConversions) {
|
|
66
|
+
// Skip rejected conversions
|
|
67
|
+
if (wpConv.status === 'Удержан') {
|
|
68
|
+
rejectedWP.push(wpConv);
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
const keitaroMatch = keitaroMap.get(wpConv.clickId);
|
|
72
|
+
if (keitaroMatch) {
|
|
73
|
+
matches.push({ wp: wpConv, keitaro: keitaroMatch });
|
|
74
|
+
keitaroMap.delete(wpConv.clickId);
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
onlyInWP.push(wpConv);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// Remaining conversions in keitaroMap are only in Keitaro
|
|
81
|
+
// We need to check their status before adding them to onlyInKeitaro
|
|
82
|
+
for (const [_, conv] of keitaroMap) {
|
|
83
|
+
if (conv.status === 'rejected') {
|
|
84
|
+
rejectedKeitaro.push(conv);
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
onlyInKeitaro.push(conv);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return {
|
|
91
|
+
wpConversions: wpConversions.filter(conv => conv.status !== 'Удержан'),
|
|
92
|
+
keitaroConversions: keitaroConversions.filter(conv => conv.status !== 'rejected'),
|
|
93
|
+
matches,
|
|
94
|
+
mismatches: {
|
|
95
|
+
onlyInWP,
|
|
96
|
+
onlyInKeitaro
|
|
97
|
+
},
|
|
98
|
+
rejected: {
|
|
99
|
+
wp: rejectedWP,
|
|
100
|
+
keitaro: rejectedKeitaro
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
exports.compareConversions = compareConversions;
|
package/package.json
CHANGED
package/src/app/app.ts
CHANGED
|
@@ -4,6 +4,15 @@ import mongoose, { Document, Model, model, PipelineStage, Schema, UpdateQuery, U
|
|
|
4
4
|
import { AlternativeFullscreen } from "app/app-integration";
|
|
5
5
|
const util = require("util")
|
|
6
6
|
|
|
7
|
+
export interface IAppsflyerPostback {
|
|
8
|
+
campaign_id: number
|
|
9
|
+
id: number
|
|
10
|
+
method: "GET" | "POST"
|
|
11
|
+
statuses: string[]
|
|
12
|
+
url: string
|
|
13
|
+
notes?: string
|
|
14
|
+
}
|
|
15
|
+
|
|
7
16
|
export interface IApp extends Document {
|
|
8
17
|
id: number
|
|
9
18
|
enabled: boolean,
|
|
@@ -42,6 +51,7 @@ export interface IApp extends Document {
|
|
|
42
51
|
ech?: boolean,
|
|
43
52
|
imageFormat: EImageFormat
|
|
44
53
|
policyPath: string
|
|
54
|
+
postbacks?: IAppsflyerPostback[]
|
|
45
55
|
}
|
|
46
56
|
|
|
47
57
|
export enum EImageFormat {
|
|
@@ -390,7 +400,34 @@ export const AppSchema = new Schema({
|
|
|
390
400
|
policyPath: {
|
|
391
401
|
type: String,
|
|
392
402
|
default: "none"
|
|
393
|
-
}
|
|
403
|
+
},
|
|
404
|
+
postbacks: [{
|
|
405
|
+
campaign_id: {
|
|
406
|
+
type: Number,
|
|
407
|
+
required: true
|
|
408
|
+
},
|
|
409
|
+
id: {
|
|
410
|
+
type: Number,
|
|
411
|
+
required: true
|
|
412
|
+
},
|
|
413
|
+
method: {
|
|
414
|
+
type: String,
|
|
415
|
+
enum: ["GET", "POST"],
|
|
416
|
+
required: true
|
|
417
|
+
},
|
|
418
|
+
statuses: [{
|
|
419
|
+
type: String,
|
|
420
|
+
required: true
|
|
421
|
+
}],
|
|
422
|
+
url: {
|
|
423
|
+
type: String,
|
|
424
|
+
required: true
|
|
425
|
+
},
|
|
426
|
+
notes: {
|
|
427
|
+
type: String,
|
|
428
|
+
required: false
|
|
429
|
+
}
|
|
430
|
+
}]
|
|
394
431
|
})
|
|
395
432
|
|
|
396
433
|
|
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, EDirectType } from "./app/app"
|
|
7
|
+
export { IAdjustEventIds, IntegrationVersion, IApp, AppSchema, PlugType, IAppKeitaroData, IExternalParams, IPlatformParams, EPlatform, AppStatus, IPublicationHistory, IRemovalInfo, EDirectType, IAppsflyerPostback } 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"
|