@bprotsyk/aso-core 2.1.180 → 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.
|
@@ -37,6 +37,7 @@ declare function getTraffleAffiliateNetworks(): Promise<IAffiliateNetwork[]>;
|
|
|
37
37
|
declare function deleteOfferById(id: number): Promise<any>;
|
|
38
38
|
declare function addOffersToTraffleKeitaro(offers: ITraffleOffer[]): Promise<void>;
|
|
39
39
|
declare function updateOfferLinkById(id: number, action_payload: string | object): Promise<any>;
|
|
40
|
+
declare function getAllCampaigns(): Promise<IKeitaroCampaign[]>;
|
|
40
41
|
declare function TrafleKeitaroParameters(parameters: any): Promise<{
|
|
41
42
|
parameters: {
|
|
42
43
|
keyword: {
|
|
@@ -133,6 +134,10 @@ declare function TrafleKeitaroParameters(parameters: any): Promise<{
|
|
|
133
134
|
} | null>;
|
|
134
135
|
declare function cloneTraffleCampaign(app: IApp, platform: EPlatform, addDefaultStreams?: boolean): Promise<IKeitaroCampaign | any>;
|
|
135
136
|
export declare function updateStreamsForApp(app: IApp, platform: EPlatform): Promise<boolean>;
|
|
137
|
+
export declare function updateCampaignNamesToNewPattern(requireConfirmation?: boolean): Promise<{
|
|
138
|
+
updated: number;
|
|
139
|
+
failed: number;
|
|
140
|
+
}>;
|
|
136
141
|
export declare const TraffleKeitaroService: {
|
|
137
142
|
addOffersToTraffleKeitaro: typeof addOffersToTraffleKeitaro;
|
|
138
143
|
getTraffleOffersGroups: typeof getTraffleOffersGroups;
|
|
@@ -140,9 +145,11 @@ export declare const TraffleKeitaroService: {
|
|
|
140
145
|
createGroup: typeof createGroup;
|
|
141
146
|
deleteOfferById: typeof deleteOfferById;
|
|
142
147
|
getAllOffers: typeof getAllOffers;
|
|
148
|
+
getAllCampaigns: typeof getAllCampaigns;
|
|
143
149
|
updateOfferLinkById: typeof updateOfferLinkById;
|
|
144
150
|
cloneTraffleCampaign: typeof cloneTraffleCampaign;
|
|
145
151
|
TrafleKeitaroParameters: typeof TrafleKeitaroParameters;
|
|
146
152
|
updateStreamsForApp: typeof updateStreamsForApp;
|
|
153
|
+
updateCampaignNamesToNewPattern: typeof updateCampaignNamesToNewPattern;
|
|
147
154
|
};
|
|
148
155
|
export {};
|
|
@@ -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
|
};
|
package/package.json
CHANGED
|
@@ -378,6 +378,27 @@ async function checkAndUpdateCampaignParameters(existingCampaign: IKeitaroCampai
|
|
|
378
378
|
}
|
|
379
379
|
|
|
380
380
|
console.log("Parameters are up to date, no update needed");
|
|
381
|
+
|
|
382
|
+
// Always add postback even if no parameter update is needed
|
|
383
|
+
try {
|
|
384
|
+
console.log(`[Traffle] Adding postback to campaign ${existingCampaign.id} (no parameter update needed)`);
|
|
385
|
+
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}`;
|
|
386
|
+
|
|
387
|
+
await keitaroApi.put(`/campaigns/${existingCampaign.id}`, {
|
|
388
|
+
postbacks: [{
|
|
389
|
+
id: 0,
|
|
390
|
+
campaign_id: existingCampaign.id,
|
|
391
|
+
method: "GET",
|
|
392
|
+
statuses: ["lead", "sale", "rejected", "rebill"],
|
|
393
|
+
url: postbackUrl
|
|
394
|
+
}]
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
console.log(`[Traffle] Postback added to campaign ${existingCampaign.id}`);
|
|
398
|
+
} catch (error) {
|
|
399
|
+
console.error(`[Traffle] Failed to add postback to campaign ${existingCampaign.id}:`, error);
|
|
400
|
+
}
|
|
401
|
+
|
|
381
402
|
return false;
|
|
382
403
|
}
|
|
383
404
|
|
|
@@ -572,6 +593,7 @@ async function updateAppWithCampaignParams(existingCampaign: IKeitaroCampaign, a
|
|
|
572
593
|
}
|
|
573
594
|
|
|
574
595
|
async function cloneTraffleCampaign(app: IApp, platform: EPlatform, addDefaultStreams?: boolean): Promise<IKeitaroCampaign | any> {
|
|
596
|
+
console.log(`[Traffle] cloneTraffleCampaign called for app ${app.id}, platform ${platform}, addDefaultStreams: ${addDefaultStreams}`)
|
|
575
597
|
|
|
576
598
|
|
|
577
599
|
|
|
@@ -722,6 +744,26 @@ async function cloneTraffleCampaign(app: IApp, platform: EPlatform, addDefaultSt
|
|
|
722
744
|
}
|
|
723
745
|
}
|
|
724
746
|
|
|
747
|
+
// Add postback to existing campaign
|
|
748
|
+
try {
|
|
749
|
+
console.log(`[Traffle] Adding postback to existing campaign ${existingCampaign.id}`);
|
|
750
|
+
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}`;
|
|
751
|
+
|
|
752
|
+
await keitaroApi.put(`/campaigns/${existingCampaign.id}`, {
|
|
753
|
+
postbacks: [{
|
|
754
|
+
id: 0,
|
|
755
|
+
campaign_id: existingCampaign.id,
|
|
756
|
+
method: "GET",
|
|
757
|
+
statuses: ["lead", "sale", "rejected", "rebill"],
|
|
758
|
+
url: postbackUrl
|
|
759
|
+
}]
|
|
760
|
+
});
|
|
761
|
+
|
|
762
|
+
console.log(`[Traffle] Postback added to campaign ${existingCampaign.id}`);
|
|
763
|
+
} catch (error) {
|
|
764
|
+
console.error(`[Traffle] Failed to add postback to campaign ${existingCampaign.id}:`, error);
|
|
765
|
+
}
|
|
766
|
+
|
|
725
767
|
console.log(existingCampaign)
|
|
726
768
|
return existingCampaign;
|
|
727
769
|
}
|
|
@@ -870,6 +912,27 @@ async function cloneTraffleCampaign(app: IApp, platform: EPlatform, addDefaultSt
|
|
|
870
912
|
}
|
|
871
913
|
|
|
872
914
|
const updatedCampaign = await getCampaignById(newCampaign.id);
|
|
915
|
+
|
|
916
|
+
// Add postback to new campaign
|
|
917
|
+
try {
|
|
918
|
+
console.log(`[Traffle] Adding postback to new campaign ${updatedCampaign.id}`);
|
|
919
|
+
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}`;
|
|
920
|
+
|
|
921
|
+
await keitaroApi.put(`/campaigns/${updatedCampaign.id}`, {
|
|
922
|
+
postbacks: [{
|
|
923
|
+
id: 0,
|
|
924
|
+
campaign_id: updatedCampaign.id,
|
|
925
|
+
method: "GET",
|
|
926
|
+
statuses: ["lead", "sale", "rejected", "rebill"],
|
|
927
|
+
url: postbackUrl
|
|
928
|
+
}]
|
|
929
|
+
});
|
|
930
|
+
|
|
931
|
+
console.log(`[Traffle] Postback added to campaign ${updatedCampaign.id}`);
|
|
932
|
+
} catch (error) {
|
|
933
|
+
console.error(`[Traffle] Failed to add postback to campaign ${updatedCampaign.id}:`, error);
|
|
934
|
+
}
|
|
935
|
+
|
|
873
936
|
return updatedCampaign;
|
|
874
937
|
}
|
|
875
938
|
|
|
@@ -1001,6 +1064,214 @@ export async function updateStreamsForApp(app: IApp, platform: EPlatform): Promi
|
|
|
1001
1064
|
}
|
|
1002
1065
|
}
|
|
1003
1066
|
|
|
1067
|
+
async function findCampaignsWithOldNamingPattern(): Promise<IKeitaroCampaign[]> {
|
|
1068
|
+
try {
|
|
1069
|
+
const allCampaigns = await getAllCampaigns();
|
|
1070
|
+
|
|
1071
|
+
// Pattern: [id] app_name [bundle] [platform] domain.com (flexible - ID is optional)
|
|
1072
|
+
// We want to find campaigns that DON'T have [Volodymyr Vinnik] in them
|
|
1073
|
+
const oldPatternCampaigns = allCampaigns.filter(campaign => {
|
|
1074
|
+
const name = campaign.name;
|
|
1075
|
+
|
|
1076
|
+
// Check if it matches the old pattern: BAN_prefix [id] app_name [bundle] [platform] domain (flexible)
|
|
1077
|
+
const oldPattern = /^(BAN[_\w.]*\s+)?(\[(\d+)\]\s+)?([^[]+)\s+\[([^\]]+)\]\s+\[([^\]]+)\]\s+(.+)$/;
|
|
1078
|
+
const match = name.match(oldPattern);
|
|
1079
|
+
|
|
1080
|
+
if (!match) return false;
|
|
1081
|
+
|
|
1082
|
+
// Check if it doesn't already have [Volodymyr Vinnik]
|
|
1083
|
+
return !name.includes('[Volodymyr Vinnik]');
|
|
1084
|
+
});
|
|
1085
|
+
|
|
1086
|
+
console.log(`Found ${oldPatternCampaigns.length} campaigns with old naming pattern`);
|
|
1087
|
+
return oldPatternCampaigns;
|
|
1088
|
+
} catch (error) {
|
|
1089
|
+
console.error("Error finding campaigns with old naming pattern:", error);
|
|
1090
|
+
return [];
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
async function updateCampaignNameToNewPattern(campaign: IKeitaroCampaign, requireConfirmation: boolean = false): Promise<boolean> {
|
|
1095
|
+
try {
|
|
1096
|
+
const name = campaign.name;
|
|
1097
|
+
|
|
1098
|
+
// Parse the old pattern: BAN_prefix [id] app_name [bundle] [platform] domain (flexible - BAN and ID are optional)
|
|
1099
|
+
const oldPattern = /^(BAN[_\w.]*\s+)?(\[(\d+)\]\s+)?([^[]+)\s+\[([^\]]+)\]\s+\[([^\]]+)\]\s+(.+)$/;
|
|
1100
|
+
const match = name.match(oldPattern);
|
|
1101
|
+
|
|
1102
|
+
if (!match) {
|
|
1103
|
+
console.log(`Campaign ${campaign.id} doesn't match old pattern: ${name}`);
|
|
1104
|
+
return false;
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
const [, banPrefix, , idFromName, appName, bundle, platform, domain] = match;
|
|
1108
|
+
|
|
1109
|
+
// Create new name based on what exists in original name
|
|
1110
|
+
let newName: string;
|
|
1111
|
+
if (banPrefix && idFromName) {
|
|
1112
|
+
// Original had BAN and ID: BAN_prefix [id] [bundle] app_name [Volodymyr Vinnik] [El Trafico] [platform] domain
|
|
1113
|
+
newName = `${banPrefix.trim()} [${idFromName}] [${bundle}] ${appName.trim()} [Volodymyr Vinnik] [El Trafico] [${platform}] ${domain}`;
|
|
1114
|
+
} else if (banPrefix && !idFromName) {
|
|
1115
|
+
// Original had BAN but no ID: BAN_prefix [bundle] app_name [Volodymyr Vinnik] [El Trafico] [platform] domain
|
|
1116
|
+
newName = `${banPrefix.trim()} [${bundle}] ${appName.trim()} [Volodymyr Vinnik] [El Trafico] [${platform}] ${domain}`;
|
|
1117
|
+
} else if (!banPrefix && idFromName) {
|
|
1118
|
+
// Original had ID but no BAN: [id] [bundle] app_name [Volodymyr Vinnik] [El Trafico] [platform] domain
|
|
1119
|
+
newName = `[${idFromName}] [${bundle}] ${appName.trim()} [Volodymyr Vinnik] [El Trafico] [${platform}] ${domain}`;
|
|
1120
|
+
} else {
|
|
1121
|
+
// Original had neither BAN nor ID: [bundle] app_name [Volodymyr Vinnik] [El Trafico] [platform] domain
|
|
1122
|
+
newName = `[${bundle}] ${appName.trim()} [Volodymyr Vinnik] [El Trafico] [${platform}] ${domain}`;
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
if (requireConfirmation) {
|
|
1126
|
+
console.log(`\nCampaign ${campaign.id}:`);
|
|
1127
|
+
console.log(`OLD: "${name}"`);
|
|
1128
|
+
console.log(`NEW: "${newName}"`);
|
|
1129
|
+
console.log(`\nDo you want to update this campaign? (y/n/s to skip all remaining):`);
|
|
1130
|
+
|
|
1131
|
+
const confirmed = await confirmUpdate();
|
|
1132
|
+
if (!confirmed) {
|
|
1133
|
+
console.log(`Skipped campaign ${campaign.id}`);
|
|
1134
|
+
return false;
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
// Update the campaign
|
|
1139
|
+
await keitaroApi.put(`/campaigns/${campaign.id}`, { name: newName });
|
|
1140
|
+
console.log(`Updated campaign ${campaign.id}: "${name}" → "${newName}"`);
|
|
1141
|
+
|
|
1142
|
+
return true;
|
|
1143
|
+
} catch (error) {
|
|
1144
|
+
console.error(`Failed to update campaign ${campaign.id}:`, error);
|
|
1145
|
+
return false;
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
// Helper function to handle user confirmation
|
|
1150
|
+
async function confirmUpdate(): Promise<boolean> {
|
|
1151
|
+
const readline = require('readline');
|
|
1152
|
+
const rl = readline.createInterface({
|
|
1153
|
+
input: process.stdin,
|
|
1154
|
+
output: process.stdout
|
|
1155
|
+
});
|
|
1156
|
+
|
|
1157
|
+
return new Promise((resolve) => {
|
|
1158
|
+
rl.question('', (answer: string) => {
|
|
1159
|
+
rl.close();
|
|
1160
|
+
const lowerAnswer = answer.toLowerCase().trim();
|
|
1161
|
+
if (lowerAnswer === 's') {
|
|
1162
|
+
// Skip all remaining
|
|
1163
|
+
console.log("Skipping all remaining updates...");
|
|
1164
|
+
process.exit(0);
|
|
1165
|
+
}
|
|
1166
|
+
resolve(lowerAnswer === 'y' || lowerAnswer === 'yes');
|
|
1167
|
+
});
|
|
1168
|
+
});
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
export async function updateCampaignNamesToNewPattern(requireConfirmation: boolean = false): Promise<{ updated: number, failed: number }> {
|
|
1172
|
+
try {
|
|
1173
|
+
console.log("Starting campaign name update process...");
|
|
1174
|
+
|
|
1175
|
+
const oldPatternCampaigns = await findCampaignsWithOldNamingPattern();
|
|
1176
|
+
|
|
1177
|
+
if (oldPatternCampaigns.length === 0) {
|
|
1178
|
+
console.log("No campaigns found with old naming pattern");
|
|
1179
|
+
return { updated: 0, failed: 0 };
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
console.log(`Found ${oldPatternCampaigns.length} campaigns to potentially update`);
|
|
1183
|
+
|
|
1184
|
+
if (requireConfirmation) {
|
|
1185
|
+
console.log("Interactive mode: You will be asked to confirm each update");
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
let updated = 0;
|
|
1189
|
+
let failed = 0;
|
|
1190
|
+
|
|
1191
|
+
for (const campaign of oldPatternCampaigns) {
|
|
1192
|
+
const success = await updateCampaignNameToNewPattern(campaign, requireConfirmation);
|
|
1193
|
+
if (success) {
|
|
1194
|
+
updated++;
|
|
1195
|
+
} else {
|
|
1196
|
+
failed++;
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
console.log(`Campaign name update completed: ${updated} updated, ${failed} failed`);
|
|
1201
|
+
return { updated, failed };
|
|
1202
|
+
} catch (error) {
|
|
1203
|
+
console.error("Failed to update campaign names:", error);
|
|
1204
|
+
return { updated: 0, failed: 0 };
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
async function ensurePostbacksForCampaign(campaignId: number, appId: number, bundle: string): Promise<void> {
|
|
1209
|
+
try {
|
|
1210
|
+
console.log(`[Traffle] Starting ensurePostbacksForCampaign for campaign ${campaignId}, app ${appId}, bundle ${bundle}`)
|
|
1211
|
+
|
|
1212
|
+
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}`
|
|
1213
|
+
|
|
1214
|
+
// Check if postback already exists
|
|
1215
|
+
console.log(`[Traffle] Getting existing postbacks for campaign ${campaignId}`)
|
|
1216
|
+
const existingPostbacks = await getCampaignPostbacks(campaignId)
|
|
1217
|
+
console.log(`[Traffle] Existing postbacks:`, existingPostbacks)
|
|
1218
|
+
|
|
1219
|
+
const postbackExists = existingPostbacks?.some(postback =>
|
|
1220
|
+
postback.url === postbackUrl &&
|
|
1221
|
+
postback.method === "GET" &&
|
|
1222
|
+
postback.statuses?.includes("lead")
|
|
1223
|
+
)
|
|
1224
|
+
|
|
1225
|
+
if (postbackExists) {
|
|
1226
|
+
console.log(`[Traffle] AppsFlyer postback already exists for Traffle campaign ${campaignId}, skipping`)
|
|
1227
|
+
return
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
const postbackData = {
|
|
1231
|
+
id: 0, // Will be set by Traffle
|
|
1232
|
+
campaign_id: campaignId,
|
|
1233
|
+
method: "GET",
|
|
1234
|
+
statuses: ["lead", "sale", "rejected", "rebill"],
|
|
1235
|
+
url: postbackUrl
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
console.log(`[Traffle] Adding AppsFlyer postback to Traffle campaign ${campaignId}:`, postbackData)
|
|
1239
|
+
|
|
1240
|
+
// Use Traffle API to add postback via campaign update
|
|
1241
|
+
console.log(`[Traffle] Making PUT request to /campaigns/${campaignId} with postbacks:`, [postbackData])
|
|
1242
|
+
|
|
1243
|
+
const response = await keitaroApi.put(`/campaigns/${campaignId}`, {
|
|
1244
|
+
postbacks: [postbackData]
|
|
1245
|
+
})
|
|
1246
|
+
|
|
1247
|
+
console.log(`[Traffle] Postback creation response:`, response.status, response.data?.postbacks)
|
|
1248
|
+
|
|
1249
|
+
if (response.status === 200) {
|
|
1250
|
+
console.log(`[Traffle] Successfully added AppsFlyer postback to Traffle campaign ${campaignId}`)
|
|
1251
|
+
}
|
|
1252
|
+
} catch (error) {
|
|
1253
|
+
console.error(`Failed to add AppsFlyer postback to Traffle campaign ${campaignId}:`, error)
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
async function getCampaignPostbacks(campaignId: number): Promise<any[] | null> {
|
|
1258
|
+
try {
|
|
1259
|
+
console.log(`[Traffle] Getting postbacks for campaign ${campaignId}`)
|
|
1260
|
+
const response = await keitaroApi.get(`/campaigns/${campaignId}`)
|
|
1261
|
+
|
|
1262
|
+
console.log(`[Traffle] Campaign ${campaignId} response:`, response.status, response.data?.postbacks)
|
|
1263
|
+
|
|
1264
|
+
if (response.status === 200 && response.data) {
|
|
1265
|
+
return response.data.postbacks || []
|
|
1266
|
+
}
|
|
1267
|
+
|
|
1268
|
+
return null
|
|
1269
|
+
} catch (error) {
|
|
1270
|
+
console.error(`[Traffle] Failed to get postbacks for Traffle campaign ${campaignId}:`, error)
|
|
1271
|
+
return null
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
|
|
1004
1275
|
export const TraffleKeitaroService = {
|
|
1005
|
-
addOffersToTraffleKeitaro, getTraffleOffersGroups, getTraffleAffiliateNetworks, createGroup, deleteOfferById, getAllOffers, updateOfferLinkById, cloneTraffleCampaign, TrafleKeitaroParameters, updateStreamsForApp
|
|
1276
|
+
addOffersToTraffleKeitaro, getTraffleOffersGroups, getTraffleAffiliateNetworks, createGroup, deleteOfferById, getAllOffers, getAllCampaigns, updateOfferLinkById, cloneTraffleCampaign, TrafleKeitaroParameters, updateStreamsForApp, updateCampaignNamesToNewPattern
|
|
1006
1277
|
}
|