@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.
Files changed (2) hide show
  1. package/api.js +41 -41
  2. 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("/me", { query: { fields: "id,name", ...(query || {}) } }, cfg);
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
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saltcorn/meta-marketing-api",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Read ads on Facebook and Instagram with the Meta Marketing API",
5
5
  "main": "index.js",
6
6
  "dependencies": {