@saltcorn/meta-marketing-api 0.1.2 → 0.1.3
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/api.js +41 -41
- package/package.json +1 -1
package/api.js
CHANGED
|
@@ -242,7 +242,7 @@ const mkApiError = (error, status) => {
|
|
|
242
242
|
const e = new Error(
|
|
243
243
|
`Meta Marketing API error (${bits.join(", ")}): ${
|
|
244
244
|
error?.error_user_msg || error?.message || "unknown error"
|
|
245
|
-
}
|
|
245
|
+
}`,
|
|
246
246
|
);
|
|
247
247
|
e.metaError = error;
|
|
248
248
|
e.status = status;
|
|
@@ -282,8 +282,7 @@ const graphFetch = async (path, opts = {}, cfg = {}) => {
|
|
|
282
282
|
if (!noAuth && cfg?.access_token)
|
|
283
283
|
headers.Authorization = `Bearer ${cfg.access_token}`;
|
|
284
284
|
|
|
285
|
-
const maxRetries =
|
|
286
|
-
typeof cfg?.max_retries === "number" ? cfg.max_retries : 3;
|
|
285
|
+
const maxRetries = typeof cfg?.max_retries === "number" ? cfg.max_retries : 3;
|
|
287
286
|
let attempt = 0;
|
|
288
287
|
|
|
289
288
|
for (;;) {
|
|
@@ -301,10 +300,10 @@ const graphFetch = async (path, opts = {}, cfg = {}) => {
|
|
|
301
300
|
}
|
|
302
301
|
console.error(
|
|
303
302
|
`Meta Marketing API non-JSON response (HTTP ${response.status})`,
|
|
304
|
-
body
|
|
303
|
+
body,
|
|
305
304
|
);
|
|
306
305
|
throw new Error(
|
|
307
|
-
`Meta Marketing API: not a JSON response (HTTP ${response.status})
|
|
306
|
+
`Meta Marketing API: not a JSON response (HTTP ${response.status})`,
|
|
308
307
|
);
|
|
309
308
|
}
|
|
310
309
|
if (json && json.error) {
|
|
@@ -334,6 +333,8 @@ const getAllPages = async (path, query, cfg = {}, opts = {}) => {
|
|
|
334
333
|
const rows = [...(json?.data || [])];
|
|
335
334
|
let pages = 1;
|
|
336
335
|
while (json?.paging?.next && pages < maxPages) {
|
|
336
|
+
if (cfg?.page_delay)
|
|
337
|
+
await new Promise((resolve) => setTimeout(resolve, cfg?.page_delay));
|
|
337
338
|
json = await graphFetch(json.paging.next, {}, cfg);
|
|
338
339
|
rows.push(...(json?.data || []));
|
|
339
340
|
pages += 1;
|
|
@@ -358,7 +359,11 @@ const withInsightsFields = (query) => {
|
|
|
358
359
|
//
|
|
359
360
|
|
|
360
361
|
const getMe = async (query, cfg) =>
|
|
361
|
-
await graphFetch(
|
|
362
|
+
await graphFetch(
|
|
363
|
+
"/me",
|
|
364
|
+
{ query: { fields: "id,name", ...(query || {}) } },
|
|
365
|
+
cfg,
|
|
366
|
+
);
|
|
362
367
|
|
|
363
368
|
const getAdAccounts = async (query, cfg) =>
|
|
364
369
|
await getAllPages("/me/adaccounts", withFields(query, "adaccount"), cfg);
|
|
@@ -367,7 +372,7 @@ const getAdAccount = async (accountId, query, cfg) =>
|
|
|
367
372
|
await graphFetch(
|
|
368
373
|
`/${actId(accountId)}`,
|
|
369
374
|
{ query: withFields(query, "adaccount") },
|
|
370
|
-
cfg
|
|
375
|
+
cfg,
|
|
371
376
|
);
|
|
372
377
|
|
|
373
378
|
const getBusinesses = async (query, cfg) =>
|
|
@@ -377,7 +382,7 @@ const getBusinessAdAccounts = async (businessId, query, cfg) =>
|
|
|
377
382
|
await getAllPages(
|
|
378
383
|
`/${businessId}/owned_ad_accounts`,
|
|
379
384
|
withFields(query, "adaccount"),
|
|
380
|
-
cfg
|
|
385
|
+
cfg,
|
|
381
386
|
);
|
|
382
387
|
|
|
383
388
|
//
|
|
@@ -388,29 +393,25 @@ const getCampaigns = async (accountId, query, cfg) =>
|
|
|
388
393
|
await getAllPages(
|
|
389
394
|
`/${actId(accountId)}/campaigns`,
|
|
390
395
|
withFields(query, "campaign"),
|
|
391
|
-
cfg
|
|
396
|
+
cfg,
|
|
392
397
|
);
|
|
393
398
|
|
|
394
399
|
const getCampaign = async (campaignId, query, cfg) =>
|
|
395
400
|
await graphFetch(
|
|
396
401
|
`/${campaignId}`,
|
|
397
402
|
{ query: withFields(query, "campaign") },
|
|
398
|
-
cfg
|
|
403
|
+
cfg,
|
|
399
404
|
);
|
|
400
405
|
|
|
401
406
|
const getAdSets = async (accountId, query, cfg) =>
|
|
402
407
|
await getAllPages(
|
|
403
408
|
`/${actId(accountId)}/adsets`,
|
|
404
409
|
withFields(query, "adset"),
|
|
405
|
-
cfg
|
|
410
|
+
cfg,
|
|
406
411
|
);
|
|
407
412
|
|
|
408
413
|
const getCampaignAdSets = async (campaignId, query, cfg) =>
|
|
409
|
-
await getAllPages(
|
|
410
|
-
`/${campaignId}/adsets`,
|
|
411
|
-
withFields(query, "adset"),
|
|
412
|
-
cfg
|
|
413
|
-
);
|
|
414
|
+
await getAllPages(`/${campaignId}/adsets`, withFields(query, "adset"), cfg);
|
|
414
415
|
|
|
415
416
|
const getAdSet = async (adSetId, query, cfg) =>
|
|
416
417
|
await graphFetch(`/${adSetId}`, { query: withFields(query, "adset") }, cfg);
|
|
@@ -431,14 +432,14 @@ const getAdCreatives = async (accountId, query, cfg) =>
|
|
|
431
432
|
await getAllPages(
|
|
432
433
|
`/${actId(accountId)}/adcreatives`,
|
|
433
434
|
withFields(query, "adcreative"),
|
|
434
|
-
cfg
|
|
435
|
+
cfg,
|
|
435
436
|
);
|
|
436
437
|
|
|
437
438
|
const getAdCreative = async (creativeId, query, cfg) =>
|
|
438
439
|
await graphFetch(
|
|
439
440
|
`/${creativeId}`,
|
|
440
441
|
{ query: withFields(query, "adcreative") },
|
|
441
|
-
cfg
|
|
442
|
+
cfg,
|
|
442
443
|
);
|
|
443
444
|
|
|
444
445
|
/** Rendered HTML preview of an ad, as an iframe snippet */
|
|
@@ -446,7 +447,7 @@ const getAdPreview = async (adId, adFormat, cfg) => {
|
|
|
446
447
|
const json = await graphFetch(
|
|
447
448
|
`/${adId}/previews`,
|
|
448
449
|
{ query: { ad_format: adFormat || "DESKTOP_FEED_STANDARD" } },
|
|
449
|
-
cfg
|
|
450
|
+
cfg,
|
|
450
451
|
);
|
|
451
452
|
return json?.data?.[0]?.body || "";
|
|
452
453
|
};
|
|
@@ -509,7 +510,7 @@ const getPageAccessToken = async (pageId, cfg) => {
|
|
|
509
510
|
const json = await graphFetch(
|
|
510
511
|
`/${pageId}`,
|
|
511
512
|
{ query: { fields: "access_token" } },
|
|
512
|
-
cfg
|
|
513
|
+
cfg,
|
|
513
514
|
);
|
|
514
515
|
token = json?.access_token || null;
|
|
515
516
|
} catch (e) {
|
|
@@ -544,7 +545,7 @@ const storyText = async (storyId, cfg) => {
|
|
|
544
545
|
const json = await graphFetch(
|
|
545
546
|
`/${storyId}`,
|
|
546
547
|
{ query: { fields: "message,attachments{title,description}" } },
|
|
547
|
-
cfg
|
|
548
|
+
cfg,
|
|
548
549
|
);
|
|
549
550
|
const attachment = json?.attachments?.data?.[0] || {};
|
|
550
551
|
return {
|
|
@@ -569,7 +570,7 @@ const getAdText = async (ad, cfg, opts = {}) => {
|
|
|
569
570
|
const fetched = await graphFetch(
|
|
570
571
|
`/${typeof ad === "object" ? ad?.id : ad}`,
|
|
571
572
|
{ query: { fields: `creative{${CREATIVE_TEXT_FIELDS.join(",")}}` } },
|
|
572
|
-
cfg
|
|
573
|
+
cfg,
|
|
573
574
|
);
|
|
574
575
|
creative = fetched?.creative;
|
|
575
576
|
}
|
|
@@ -616,7 +617,7 @@ const MEDIA_IDS = ["video_id", "image_hash", "url"];
|
|
|
616
617
|
const addMedia = (out, m) => {
|
|
617
618
|
if (!m || !MEDIA_IDS.some((k) => m[k])) return;
|
|
618
619
|
const same = out.find(
|
|
619
|
-
(o) => o.kind === m.kind && MEDIA_IDS.some((k) => m[k] && o[k] === m[k])
|
|
620
|
+
(o) => o.kind === m.kind && MEDIA_IDS.some((k) => m[k] && o[k] === m[k]),
|
|
620
621
|
);
|
|
621
622
|
if (!same) {
|
|
622
623
|
out.push(m);
|
|
@@ -686,10 +687,10 @@ const creativeMedia = (creative) => {
|
|
|
686
687
|
video_id: v.video_id,
|
|
687
688
|
thumbnail_url: v.thumbnail_url,
|
|
688
689
|
thumbnail_hash: v.thumbnail_hash,
|
|
689
|
-
})
|
|
690
|
+
}),
|
|
690
691
|
);
|
|
691
692
|
(feed.images || []).forEach((i) =>
|
|
692
|
-
addMedia(out, { kind: "image", url: i.url, image_hash: i.hash })
|
|
693
|
+
addMedia(out, { kind: "image", url: i.url, image_hash: i.hash }),
|
|
693
694
|
);
|
|
694
695
|
|
|
695
696
|
// Last, what the creative says about itself. On many ads this is the same
|
|
@@ -724,7 +725,7 @@ const storyMedia = async (storyId, cfg) => {
|
|
|
724
725
|
"attachments{media_type,media,target,url,subattachments{media_type,media,target}}",
|
|
725
726
|
},
|
|
726
727
|
},
|
|
727
|
-
cfg
|
|
728
|
+
cfg,
|
|
728
729
|
);
|
|
729
730
|
const out = [];
|
|
730
731
|
let album = false;
|
|
@@ -770,7 +771,7 @@ const postMedia = async (creative, cfg, opts = {}) => {
|
|
|
770
771
|
creative,
|
|
771
772
|
storyId,
|
|
772
773
|
cfg,
|
|
773
|
-
opts
|
|
774
|
+
opts,
|
|
774
775
|
);
|
|
775
776
|
let reason;
|
|
776
777
|
for (const useCfg of cfgs) {
|
|
@@ -807,7 +808,7 @@ const resolveMediaUrls = async (media, accountId, cfg) => {
|
|
|
807
808
|
fields: "id,source,picture,permalink_url,length,created_time",
|
|
808
809
|
},
|
|
809
810
|
},
|
|
810
|
-
cfg
|
|
811
|
+
cfg,
|
|
811
812
|
);
|
|
812
813
|
m.url = v?.source || m.url;
|
|
813
814
|
m.thumbnail_url = m.thumbnail_url || v?.picture;
|
|
@@ -846,7 +847,7 @@ const resolveMediaUrls = async (media, accountId, cfg) => {
|
|
|
846
847
|
fields: "hash,url,permalink_url,width,height",
|
|
847
848
|
},
|
|
848
849
|
},
|
|
849
|
-
cfg
|
|
850
|
+
cfg,
|
|
850
851
|
);
|
|
851
852
|
// Depending on the API version this comes back as a list or as an
|
|
852
853
|
// object keyed by hash
|
|
@@ -858,7 +859,7 @@ const resolveMediaUrls = async (media, accountId, cfg) => {
|
|
|
858
859
|
});
|
|
859
860
|
} catch (e) {
|
|
860
861
|
reason = `could not read the pictures of ad account ${actId(
|
|
861
|
-
accountId
|
|
862
|
+
accountId,
|
|
862
863
|
)}: ${e.message}`;
|
|
863
864
|
if (cfg?.log_requests) console.log(`Meta: ${reason}`);
|
|
864
865
|
}
|
|
@@ -889,7 +890,7 @@ const mediaErrors = (media) => {
|
|
|
889
890
|
if (!stuck.length) return undefined;
|
|
890
891
|
const reasons = [...new Set(stuck.map((m) => m.error))];
|
|
891
892
|
return `${stuck.length} of ${media.length} could not be reached: ${reasons.join(
|
|
892
|
-
"; "
|
|
893
|
+
"; ",
|
|
893
894
|
)}`;
|
|
894
895
|
};
|
|
895
896
|
|
|
@@ -948,7 +949,7 @@ const getAdMedia = async (ad, cfg, opts = {}) => {
|
|
|
948
949
|
thumbnail_height: opts.thumbnail_height || 1200,
|
|
949
950
|
},
|
|
950
951
|
},
|
|
951
|
-
cfg
|
|
952
|
+
cfg,
|
|
952
953
|
);
|
|
953
954
|
creative = fetched?.creative || creative;
|
|
954
955
|
accountId = accountId || fetched?.account_id || creative?.account_id;
|
|
@@ -1023,7 +1024,7 @@ const getInsights = async (objectId, query, cfg, opts) =>
|
|
|
1023
1024
|
`/${objectId}/insights`,
|
|
1024
1025
|
withInsightsFields(query),
|
|
1025
1026
|
cfg,
|
|
1026
|
-
opts
|
|
1027
|
+
opts,
|
|
1027
1028
|
);
|
|
1028
1029
|
|
|
1029
1030
|
/** Kick off an asynchronous insights job, returns { report_run_id } */
|
|
@@ -1031,7 +1032,7 @@ const startInsightsReport = async (objectId, query, cfg) =>
|
|
|
1031
1032
|
await graphFetch(
|
|
1032
1033
|
`/${objectId}/insights`,
|
|
1033
1034
|
{ method: "POST", query: withInsightsFields(query) },
|
|
1034
|
-
cfg
|
|
1035
|
+
cfg,
|
|
1035
1036
|
);
|
|
1036
1037
|
|
|
1037
1038
|
const getReportRun = async (reportRunId, cfg) =>
|
|
@@ -1039,11 +1040,10 @@ const getReportRun = async (reportRunId, cfg) =>
|
|
|
1039
1040
|
`/${reportRunId}`,
|
|
1040
1041
|
{
|
|
1041
1042
|
query: {
|
|
1042
|
-
fields:
|
|
1043
|
-
"id,async_status,async_percent_completion,date_start,date_stop",
|
|
1043
|
+
fields: "id,async_status,async_percent_completion,date_start,date_stop",
|
|
1044
1044
|
},
|
|
1045
1045
|
},
|
|
1046
|
-
cfg
|
|
1046
|
+
cfg,
|
|
1047
1047
|
);
|
|
1048
1048
|
|
|
1049
1049
|
const getReportRunInsights = async (reportRunId, query, cfg, opts) =>
|
|
@@ -1066,11 +1066,11 @@ const getInsightsAsync = async (objectId, query, cfg, opts = {}) => {
|
|
|
1066
1066
|
if (run?.async_status === "Job Completed") break;
|
|
1067
1067
|
if (["Job Failed", "Job Skipped"].includes(run?.async_status))
|
|
1068
1068
|
throw new Error(
|
|
1069
|
-
`Meta Marketing API: insights job ${run.async_status} (${report_run_id})
|
|
1069
|
+
`Meta Marketing API: insights job ${run.async_status} (${report_run_id})`,
|
|
1070
1070
|
);
|
|
1071
1071
|
if (Date.now() - startedAt > timeout)
|
|
1072
1072
|
throw new Error(
|
|
1073
|
-
`Meta Marketing API: insights job timed out (${report_run_id})
|
|
1073
|
+
`Meta Marketing API: insights job timed out (${report_run_id})`,
|
|
1074
1074
|
);
|
|
1075
1075
|
await sleep(pollInterval);
|
|
1076
1076
|
}
|
|
@@ -1094,7 +1094,7 @@ const exchangeLongLivedToken = async (app_id, app_secret, access_token, cfg) =>
|
|
|
1094
1094
|
fb_exchange_token: access_token,
|
|
1095
1095
|
},
|
|
1096
1096
|
},
|
|
1097
|
-
cfg
|
|
1097
|
+
cfg,
|
|
1098
1098
|
);
|
|
1099
1099
|
|
|
1100
1100
|
/** Inspect a token: which app it belongs to, when it expires, its scopes */
|
|
@@ -1108,7 +1108,7 @@ const debugToken = async (token, app_id, app_secret, cfg) => {
|
|
|
1108
1108
|
access_token: `${app_id}|${app_secret}`,
|
|
1109
1109
|
},
|
|
1110
1110
|
},
|
|
1111
|
-
cfg
|
|
1111
|
+
cfg,
|
|
1112
1112
|
);
|
|
1113
1113
|
return json?.data || json;
|
|
1114
1114
|
};
|