@iann29/rastro 0.10.9 → 0.11.0
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/README.md +8 -0
- package/agent/integration.md +45 -0
- package/agent/manifest.json +4 -4
- package/dist/client/federation.d.ts +82 -9
- package/dist/client/federation.d.ts.map +1 -1
- package/dist/client/federation.js +40 -2
- package/dist/client/federation.js.map +1 -1
- package/dist/client/index.d.ts +464 -8
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +259 -7
- package/dist/client/index.js.map +1 -1
- package/dist/component/_generated/api.d.ts +10 -0
- package/dist/component/_generated/api.d.ts.map +1 -1
- package/dist/component/_generated/api.js.map +1 -1
- package/dist/component/_generated/component.d.ts +273 -21
- package/dist/component/_generated/component.d.ts.map +1 -1
- package/dist/component/_generated/server.d.ts +2 -0
- package/dist/component/_generated/server.d.ts.map +1 -1
- package/dist/component/_generated/server.js.map +1 -1
- package/dist/component/constants.d.ts +31 -0
- package/dist/component/constants.d.ts.map +1 -1
- package/dist/component/constants.js +31 -0
- package/dist/component/constants.js.map +1 -1
- package/dist/component/convex.config.d.ts +2 -0
- package/dist/component/convex.config.js +7 -0
- package/dist/component/convex.config.js.map +1 -1
- package/dist/component/errors.d.ts +1 -1
- package/dist/component/errors.d.ts.map +1 -1
- package/dist/component/errors.js.map +1 -1
- package/dist/component/http.d.ts.map +1 -1
- package/dist/component/http.js +64 -0
- package/dist/component/http.js.map +1 -1
- package/dist/component/leadAdsValidators.d.ts +1092 -0
- package/dist/component/leadAdsValidators.d.ts.map +1 -0
- package/dist/component/leadAdsValidators.js +304 -0
- package/dist/component/leadAdsValidators.js.map +1 -0
- package/dist/component/leadFormRules.d.ts +60 -0
- package/dist/component/leadFormRules.d.ts.map +1 -0
- package/dist/component/leadFormRules.js +140 -0
- package/dist/component/leadFormRules.js.map +1 -0
- package/dist/component/leadFormStore.d.ts +16 -0
- package/dist/component/leadFormStore.d.ts.map +1 -0
- package/dist/component/leadFormStore.js +92 -0
- package/dist/component/leadFormStore.js.map +1 -0
- package/dist/component/leadgenToken.d.ts +56 -0
- package/dist/component/leadgenToken.d.ts.map +1 -0
- package/dist/component/leadgenToken.js +262 -0
- package/dist/component/leadgenToken.js.map +1 -0
- package/dist/component/leads.d.ts +76 -7
- package/dist/component/leads.d.ts.map +1 -1
- package/dist/component/leads.js +112 -78
- package/dist/component/leads.js.map +1 -1
- package/dist/component/meta.d.ts.map +1 -1
- package/dist/component/meta.js +21 -1
- package/dist/component/meta.js.map +1 -1
- package/dist/component/metaFake.d.ts +44 -0
- package/dist/component/metaFake.d.ts.map +1 -1
- package/dist/component/metaFake.js +400 -1
- package/dist/component/metaFake.js.map +1 -1
- package/dist/component/metaGraph.d.ts +91 -0
- package/dist/component/metaGraph.d.ts.map +1 -1
- package/dist/component/metaGraph.js +217 -0
- package/dist/component/metaGraph.js.map +1 -1
- package/dist/component/metaLeads.d.ts +681 -0
- package/dist/component/metaLeads.d.ts.map +1 -0
- package/dist/component/metaLeads.js +2634 -0
- package/dist/component/metaLeads.js.map +1 -0
- package/dist/component/schema.d.ts +414 -5
- package/dist/component/schema.d.ts.map +1 -1
- package/dist/component/schema.js +169 -1
- package/dist/component/schema.js.map +1 -1
- package/dist/component/validators.d.ts +235 -11
- package/dist/component/validators.d.ts.map +1 -1
- package/dist/component/validators.js +14 -1
- package/dist/component/validators.js.map +1 -1
- package/dist/tracker/generated.d.ts +1 -1
- package/dist/tracker/generated.js +1 -1
- package/dist/ui/campaigns/CampaignsView.d.ts +5 -1
- package/dist/ui/campaigns/CampaignsView.d.ts.map +1 -1
- package/dist/ui/campaigns/CampaignsView.js.map +1 -1
- package/dist/ui/campaigns/Drawer.d.ts.map +1 -1
- package/dist/ui/campaigns/Drawer.js +8 -26
- package/dist/ui/campaigns/Drawer.js.map +1 -1
- package/dist/ui/campaigns/labels.d.ts +29 -1
- package/dist/ui/campaigns/labels.d.ts.map +1 -1
- package/dist/ui/campaigns/labels.js +77 -2
- package/dist/ui/campaigns/labels.js.map +1 -1
- package/dist/ui/campaigns/model.js +2 -2
- package/dist/ui/campaigns/model.js.map +1 -1
- package/dist/ui/forms/FirstContact.d.ts +19 -0
- package/dist/ui/forms/FirstContact.d.ts.map +1 -0
- package/dist/ui/forms/FirstContact.js +81 -0
- package/dist/ui/forms/FirstContact.js.map +1 -0
- package/dist/ui/forms/FormLead.d.ts +28 -0
- package/dist/ui/forms/FormLead.d.ts.map +1 -0
- package/dist/ui/forms/FormLead.js +199 -0
- package/dist/ui/forms/FormLead.js.map +1 -0
- package/dist/ui/forms/FormRulesDrawer.d.ts +49 -0
- package/dist/ui/forms/FormRulesDrawer.d.ts.map +1 -0
- package/dist/ui/forms/FormRulesDrawer.js +149 -0
- package/dist/ui/forms/FormRulesDrawer.js.map +1 -0
- package/dist/ui/forms/FormsView.d.ts +10 -0
- package/dist/ui/forms/FormsView.d.ts.map +1 -0
- package/dist/ui/forms/FormsView.js +215 -0
- package/dist/ui/forms/FormsView.js.map +1 -0
- package/dist/ui/forms/Icons.d.ts +46 -0
- package/dist/ui/forms/Icons.d.ts.map +1 -0
- package/dist/ui/forms/Icons.js +45 -0
- package/dist/ui/forms/Icons.js.map +1 -0
- package/dist/ui/forms/RuleRow.d.ts +37 -0
- package/dist/ui/forms/RuleRow.d.ts.map +1 -0
- package/dist/ui/forms/RuleRow.js +80 -0
- package/dist/ui/forms/RuleRow.js.map +1 -0
- package/dist/ui/forms/WhatsAppButtonGuide.d.ts +33 -0
- package/dist/ui/forms/WhatsAppButtonGuide.d.ts.map +1 -0
- package/dist/ui/forms/WhatsAppButtonGuide.js +90 -0
- package/dist/ui/forms/WhatsAppButtonGuide.js.map +1 -0
- package/dist/ui/forms/index.d.ts +11 -0
- package/dist/ui/forms/index.d.ts.map +1 -0
- package/dist/ui/forms/index.js +7 -0
- package/dist/ui/forms/index.js.map +1 -0
- package/dist/ui/forms/journey.d.ts +19 -0
- package/dist/ui/forms/journey.d.ts.map +1 -0
- package/dist/ui/forms/journey.js +54 -0
- package/dist/ui/forms/journey.js.map +1 -0
- package/dist/ui/forms/model.d.ts +239 -0
- package/dist/ui/forms/model.d.ts.map +1 -0
- package/dist/ui/forms/model.js +556 -0
- package/dist/ui/forms/model.js.map +1 -0
- package/dist/ui/forms/sample.d.ts +36 -0
- package/dist/ui/forms/sample.d.ts.map +1 -0
- package/dist/ui/forms/sample.js +444 -0
- package/dist/ui/forms/sample.js.map +1 -0
- package/dist/ui/forms/types.d.ts +454 -0
- package/dist/ui/forms/types.d.ts.map +1 -0
- package/dist/ui/forms/types.js +2 -0
- package/dist/ui/forms/types.js.map +1 -0
- package/dist/ui/index.d.ts +2 -0
- package/dist/ui/index.d.ts.map +1 -1
- package/dist/ui/index.js +4 -0
- package/dist/ui/index.js.map +1 -1
- package/dist/ui/integrations/Overlay.d.ts +3 -1
- package/dist/ui/integrations/Overlay.d.ts.map +1 -1
- package/dist/ui/integrations/Overlay.js +2 -2
- package/dist/ui/integrations/Overlay.js.map +1 -1
- package/dist/ui/portfolio/PortfolioView.d.ts +4 -1
- package/dist/ui/portfolio/PortfolioView.d.ts.map +1 -1
- package/dist/ui/portfolio/PortfolioView.js.map +1 -1
- package/dist/ui/sample.d.ts +3 -1
- package/dist/ui/sample.d.ts.map +1 -1
- package/dist/ui/sample.js +2 -0
- package/dist/ui/sample.js.map +1 -1
- package/dist/ui/seed.d.ts +1 -1
- package/dist/ui/ui.css +2301 -0
- package/docs/federation.md +19 -0
- package/docs/meta-ads.md +713 -7
- package/docs/upgrading.md +70 -0
- package/llms.txt +1 -0
- package/package.json +1 -1
- package/src/component/_generated/api.ts +10 -0
- package/src/component/_generated/component.ts +289 -20
- package/src/component/_generated/server.ts +2 -0
- package/src/component/constants.ts +33 -0
- package/src/component/convex.config.ts +7 -0
- package/src/component/errors.ts +3 -1
- package/src/component/http.ts +75 -0
- package/src/component/leadAdsValidators.ts +365 -0
- package/src/component/leadFormRules.ts +202 -0
- package/src/component/leadFormStore.ts +129 -0
- package/src/component/leadgenToken.ts +348 -0
- package/src/component/leads.ts +136 -84
- package/src/component/meta.ts +21 -1
- package/src/component/metaFake.ts +485 -1
- package/src/component/metaGraph.ts +327 -0
- package/src/component/metaLeads.ts +3101 -0
- package/src/component/schema.ts +187 -1
- package/src/component/validators.ts +17 -1
- package/src/tracker/generated.ts +1 -1
|
@@ -82,6 +82,52 @@ export interface GraphSpendRow {
|
|
|
82
82
|
campaignName: string | null;
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
+
/** A Facebook Page the token can act for, with the Page's own token. */
|
|
86
|
+
export interface GraphPage {
|
|
87
|
+
pageId: string;
|
|
88
|
+
name: string;
|
|
89
|
+
/** The Page token Meta hands out with `me/accounts`; null when it did not. */
|
|
90
|
+
accessToken: string | null;
|
|
91
|
+
tasks: string[];
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** One question of an instant form. */
|
|
95
|
+
export interface GraphFormQuestion {
|
|
96
|
+
key: string;
|
|
97
|
+
label: string;
|
|
98
|
+
type: string;
|
|
99
|
+
options?: Array<{ key: string; value: string }>;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export interface GraphForm {
|
|
103
|
+
formId: string;
|
|
104
|
+
name: string;
|
|
105
|
+
status: string;
|
|
106
|
+
questions: GraphFormQuestion[];
|
|
107
|
+
/** The end screen's WhatsApp button; null when Meta does not say. */
|
|
108
|
+
whatsappButton: boolean | null;
|
|
109
|
+
createdTime: number | null;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** A form lead as Meta returns it. `fields` carries personal data. */
|
|
113
|
+
export interface GraphLead {
|
|
114
|
+
leadgenId: string;
|
|
115
|
+
createdTime: number;
|
|
116
|
+
formId: string | null;
|
|
117
|
+
adId: string | null;
|
|
118
|
+
adsetId: string | null;
|
|
119
|
+
campaignId: string | null;
|
|
120
|
+
platform: string | null;
|
|
121
|
+
isOrganic: boolean;
|
|
122
|
+
fields: Array<{ name: string; values: string[] }>;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Which Pages a login may read leads from (`leads_retrieval`): every Page it
|
|
127
|
+
* reaches (`all`), or only `pageIds`. Null when Meta did not say.
|
|
128
|
+
*/
|
|
129
|
+
export type GraphLeadAccess = { all: boolean; pageIds: string[] } | null;
|
|
130
|
+
|
|
85
131
|
/** How close the app is to Meta's rate limits, from the response headers. */
|
|
86
132
|
export interface GraphUsage {
|
|
87
133
|
/** The highest percentage any limit header reported, 0–100+. */
|
|
@@ -182,6 +228,37 @@ export interface MetaGraph {
|
|
|
182
228
|
pixelId: string,
|
|
183
229
|
payload: ConversionsPayload,
|
|
184
230
|
): Promise<{ eventsReceived: number; fbtraceId: string | null }>;
|
|
231
|
+
/**
|
|
232
|
+
* The Pages the token acts for (`me/accounts`), each with its own token.
|
|
233
|
+
* `complete` is false when the listing stopped at the page cap.
|
|
234
|
+
*/
|
|
235
|
+
pages(token: string): Promise<{ pages: GraphPage[]; complete: boolean }>;
|
|
236
|
+
/**
|
|
237
|
+
* Which Pages the login may read leads from: `debug_token`'s granular
|
|
238
|
+
* scopes with `app`, else `me/permissions`. Null when Meta did not say.
|
|
239
|
+
*/
|
|
240
|
+
leadAccess(token: string, app?: MetaApp | null): Promise<GraphLeadAccess>;
|
|
241
|
+
/** Subscribes the app to the Page's `leadgen` webhook (the Page token). */
|
|
242
|
+
subscribePage(pageToken: string, pageId: string): Promise<void>;
|
|
243
|
+
/** The Page's instant forms, every status. */
|
|
244
|
+
leadForms(
|
|
245
|
+
pageToken: string,
|
|
246
|
+
pageId: string,
|
|
247
|
+
): Promise<{ forms: GraphForm[]; usage: GraphUsage; complete: boolean }>;
|
|
248
|
+
/** One lead, with its answers (personal data: never store `fields`). */
|
|
249
|
+
lead(pageToken: string, leadgenId: string): Promise<GraphLead>;
|
|
250
|
+
/**
|
|
251
|
+
* A form's leads created after `sinceSec` and, with `untilSec`, before it
|
|
252
|
+
* (Meta's seconds). Meta lists the newest first, so a listing that stopped
|
|
253
|
+
* at the cap (`complete: false`) holds the newest of the window: read a
|
|
254
|
+
* narrower window instead of trusting it to cover the older ones.
|
|
255
|
+
*/
|
|
256
|
+
formLeads(
|
|
257
|
+
pageToken: string,
|
|
258
|
+
formId: string,
|
|
259
|
+
sinceSec: number,
|
|
260
|
+
untilSec?: number,
|
|
261
|
+
): Promise<{ leads: GraphLead[]; usage: GraphUsage; complete: boolean }>;
|
|
185
262
|
}
|
|
186
263
|
|
|
187
264
|
/** One Conversions API event, user data already hashed by the host. */
|
|
@@ -427,6 +504,124 @@ function mapAccount(raw: unknown): GraphAdAccount | null {
|
|
|
427
504
|
};
|
|
428
505
|
}
|
|
429
506
|
|
|
507
|
+
/** Meta's times (`2026-10-09T12:34:56+0000`) in milliseconds, or null. */
|
|
508
|
+
export function graphTime(value: unknown): number | null {
|
|
509
|
+
const raw = text(value);
|
|
510
|
+
if (!raw) return null;
|
|
511
|
+
const parsed = Date.parse(raw.replace(/([+-]\d{2})(\d{2})$/u, "$1:$2"));
|
|
512
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
const GRAPH_ID = /^\d{1,32}$/u;
|
|
516
|
+
|
|
517
|
+
function mapPage(raw: unknown): GraphPage | null {
|
|
518
|
+
if (!isRecord(raw)) return null;
|
|
519
|
+
const pageId = text(raw.id);
|
|
520
|
+
if (!pageId || !GRAPH_ID.test(pageId)) return null;
|
|
521
|
+
return {
|
|
522
|
+
pageId,
|
|
523
|
+
name: text(raw.name) ?? pageId,
|
|
524
|
+
accessToken: text(raw.access_token),
|
|
525
|
+
tasks: Array.isArray(raw.tasks)
|
|
526
|
+
? raw.tasks.filter((task): task is string => typeof task === "string")
|
|
527
|
+
: [],
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
function mapQuestion(raw: unknown): GraphFormQuestion | null {
|
|
532
|
+
if (!isRecord(raw)) return null;
|
|
533
|
+
const key = text(raw.key);
|
|
534
|
+
if (!key) return null;
|
|
535
|
+
const options = Array.isArray(raw.options)
|
|
536
|
+
? raw.options.flatMap((option) => {
|
|
537
|
+
if (!isRecord(option)) return [];
|
|
538
|
+
const optionKey = text(option.key) ?? text(option.value);
|
|
539
|
+
const value = text(option.value) ?? optionKey;
|
|
540
|
+
return optionKey && value ? [{ key: optionKey, value }] : [];
|
|
541
|
+
})
|
|
542
|
+
: [];
|
|
543
|
+
return {
|
|
544
|
+
key,
|
|
545
|
+
label: text(raw.label) ?? key,
|
|
546
|
+
type: text(raw.type) ?? "CUSTOM",
|
|
547
|
+
...(options.length > 0 ? { options } : {}),
|
|
548
|
+
};
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
/**
|
|
552
|
+
* Whether the form's end screen sends the lead to WhatsApp, from its button
|
|
553
|
+
* type; null when Meta does not describe the end screen's button.
|
|
554
|
+
*/
|
|
555
|
+
export function whatsappButtonOf(thankYouPage: unknown): boolean | null {
|
|
556
|
+
if (!isRecord(thankYouPage)) return null;
|
|
557
|
+
const type = text(thankYouPage.button_type);
|
|
558
|
+
if (!type) return null;
|
|
559
|
+
return /WHATSAPP/iu.test(type);
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
function mapForm(raw: unknown): GraphForm | null {
|
|
563
|
+
if (!isRecord(raw)) return null;
|
|
564
|
+
const formId = text(raw.id);
|
|
565
|
+
if (!formId || !GRAPH_ID.test(formId)) return null;
|
|
566
|
+
return {
|
|
567
|
+
formId,
|
|
568
|
+
name: text(raw.name) ?? formId,
|
|
569
|
+
status: text(raw.status)?.toUpperCase() ?? "ACTIVE",
|
|
570
|
+
questions: Array.isArray(raw.questions)
|
|
571
|
+
? raw.questions.flatMap((question) => {
|
|
572
|
+
const mapped = mapQuestion(question);
|
|
573
|
+
return mapped ? [mapped] : [];
|
|
574
|
+
})
|
|
575
|
+
: [],
|
|
576
|
+
whatsappButton: whatsappButtonOf(raw.thank_you_page),
|
|
577
|
+
createdTime: graphTime(raw.created_time),
|
|
578
|
+
};
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
function mapLead(raw: unknown): GraphLead | null {
|
|
582
|
+
if (!isRecord(raw)) return null;
|
|
583
|
+
const leadgenId = text(raw.id);
|
|
584
|
+
const createdTime = graphTime(raw.created_time);
|
|
585
|
+
if (!leadgenId || !GRAPH_ID.test(leadgenId) || createdTime === null) {
|
|
586
|
+
return null;
|
|
587
|
+
}
|
|
588
|
+
const id = (value: unknown) => {
|
|
589
|
+
const found = text(typeof value === "number" ? String(value) : value);
|
|
590
|
+
return found && GRAPH_ID.test(found) ? found : null;
|
|
591
|
+
};
|
|
592
|
+
return {
|
|
593
|
+
leadgenId,
|
|
594
|
+
createdTime,
|
|
595
|
+
formId: id(raw.form_id),
|
|
596
|
+
adId: id(raw.ad_id),
|
|
597
|
+
adsetId: id(raw.adset_id),
|
|
598
|
+
campaignId: id(raw.campaign_id),
|
|
599
|
+
platform: text(raw.platform)?.slice(0, 16) ?? null,
|
|
600
|
+
isOrganic: raw.is_organic === true,
|
|
601
|
+
fields: Array.isArray(raw.field_data)
|
|
602
|
+
? raw.field_data.flatMap((field) => {
|
|
603
|
+
if (!isRecord(field)) return [];
|
|
604
|
+
const name = text(field.name);
|
|
605
|
+
if (!name) return [];
|
|
606
|
+
const values = Array.isArray(field.values)
|
|
607
|
+
? field.values.filter(
|
|
608
|
+
(value): value is string => typeof value === "string",
|
|
609
|
+
)
|
|
610
|
+
: [];
|
|
611
|
+
return [{ name, values }];
|
|
612
|
+
})
|
|
613
|
+
: [],
|
|
614
|
+
};
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
const FORM_FIELDS =
|
|
618
|
+
"id,name,status,questions,thank_you_page{button_type},created_time";
|
|
619
|
+
const LEAD_FIELDS =
|
|
620
|
+
"id,created_time,field_data,ad_id,adset_id,campaign_id,form_id,platform,is_organic";
|
|
621
|
+
const MAX_GRAPH_PAGES = 200;
|
|
622
|
+
const MAX_FORMS = 500;
|
|
623
|
+
const MAX_FORM_LEADS = 2_000;
|
|
624
|
+
|
|
430
625
|
export function actId(adAccountId: string): string {
|
|
431
626
|
return adAccountId.startsWith("act_") ? adAccountId : `act_${adAccountId}`;
|
|
432
627
|
}
|
|
@@ -908,6 +1103,138 @@ function realMetaGraph(fetchImpl: Fetch): MetaGraph {
|
|
|
908
1103
|
fbtraceId: text(body.fbtrace_id),
|
|
909
1104
|
};
|
|
910
1105
|
},
|
|
1106
|
+
|
|
1107
|
+
async pages(token) {
|
|
1108
|
+
const { items, complete } = await pages(
|
|
1109
|
+
url("me/accounts", {
|
|
1110
|
+
fields: "id,name,access_token,tasks",
|
|
1111
|
+
limit: "100",
|
|
1112
|
+
}),
|
|
1113
|
+
token,
|
|
1114
|
+
mapPage,
|
|
1115
|
+
MAX_GRAPH_PAGES,
|
|
1116
|
+
);
|
|
1117
|
+
return { pages: items, complete };
|
|
1118
|
+
},
|
|
1119
|
+
|
|
1120
|
+
async leadAccess(token, app) {
|
|
1121
|
+
// A login's granular scopes say which Pages `leads_retrieval` covers;
|
|
1122
|
+
// without the app's token, the token's own permissions say whether
|
|
1123
|
+
// it has it at all.
|
|
1124
|
+
if (app) {
|
|
1125
|
+
try {
|
|
1126
|
+
const { body } = await call(
|
|
1127
|
+
url("debug_token", { input_token: token }),
|
|
1128
|
+
`${app.appId}|${app.appSecret}`,
|
|
1129
|
+
);
|
|
1130
|
+
const data = isRecord(body.data) ? body.data : {};
|
|
1131
|
+
const granular = Array.isArray(data.granular_scopes)
|
|
1132
|
+
? data.granular_scopes.filter(isRecord)
|
|
1133
|
+
: [];
|
|
1134
|
+
const leads = granular.find(
|
|
1135
|
+
(scope) => scope.scope === "leads_retrieval",
|
|
1136
|
+
);
|
|
1137
|
+
if (leads) {
|
|
1138
|
+
const targets = Array.isArray(leads.target_ids)
|
|
1139
|
+
? leads.target_ids.map(String).filter((id) => GRAPH_ID.test(id))
|
|
1140
|
+
: null;
|
|
1141
|
+
return targets === null || targets.length === 0
|
|
1142
|
+
? { all: true, pageIds: [] }
|
|
1143
|
+
: { all: false, pageIds: targets };
|
|
1144
|
+
}
|
|
1145
|
+
const scopes = Array.isArray(data.scopes) ? data.scopes : null;
|
|
1146
|
+
if (scopes) {
|
|
1147
|
+
return { all: scopes.includes("leads_retrieval"), pageIds: [] };
|
|
1148
|
+
}
|
|
1149
|
+
} catch (error) {
|
|
1150
|
+
if (!(error instanceof MetaGraphError)) throw error;
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
try {
|
|
1154
|
+
const { body } = await call(url("me/permissions", {}), token);
|
|
1155
|
+
const rows = Array.isArray(body.data) ? body.data.filter(isRecord) : [];
|
|
1156
|
+
if (rows.length === 0) return null;
|
|
1157
|
+
return {
|
|
1158
|
+
all: rows.some(
|
|
1159
|
+
(row) =>
|
|
1160
|
+
row.permission === "leads_retrieval" && row.status === "granted",
|
|
1161
|
+
),
|
|
1162
|
+
pageIds: [],
|
|
1163
|
+
};
|
|
1164
|
+
} catch (error) {
|
|
1165
|
+
if (error instanceof MetaGraphError) return null;
|
|
1166
|
+
throw error;
|
|
1167
|
+
}
|
|
1168
|
+
},
|
|
1169
|
+
|
|
1170
|
+
async subscribePage(pageToken, pageId) {
|
|
1171
|
+
const { body } = await call(
|
|
1172
|
+
url(`${pageId}/subscribed_apps`, { subscribed_fields: "leadgen" }),
|
|
1173
|
+
pageToken,
|
|
1174
|
+
{ subscribed_fields: "leadgen" },
|
|
1175
|
+
);
|
|
1176
|
+
// A 200 is not enough: Meta says `success: true` when it subscribed.
|
|
1177
|
+
if (body.success !== true) {
|
|
1178
|
+
throw new MetaGraphError(
|
|
1179
|
+
"other",
|
|
1180
|
+
"Meta did not confirm the Page's leadgen subscription",
|
|
1181
|
+
);
|
|
1182
|
+
}
|
|
1183
|
+
},
|
|
1184
|
+
|
|
1185
|
+
async leadForms(pageToken, pageId) {
|
|
1186
|
+
const { items, usage, complete } = await pages(
|
|
1187
|
+
url(`${pageId}/leadgen_forms`, {
|
|
1188
|
+
fields: FORM_FIELDS,
|
|
1189
|
+
limit: "100",
|
|
1190
|
+
}),
|
|
1191
|
+
pageToken,
|
|
1192
|
+
mapForm,
|
|
1193
|
+
MAX_FORMS,
|
|
1194
|
+
);
|
|
1195
|
+
return { forms: items, usage, complete };
|
|
1196
|
+
},
|
|
1197
|
+
|
|
1198
|
+
async lead(pageToken, leadgenId) {
|
|
1199
|
+
const { body } = await call(
|
|
1200
|
+
url(leadgenId, { fields: LEAD_FIELDS }),
|
|
1201
|
+
pageToken,
|
|
1202
|
+
);
|
|
1203
|
+
const lead = mapLead(body);
|
|
1204
|
+
if (!lead) {
|
|
1205
|
+
throw new MetaGraphError("other", "Meta returned an unreadable lead");
|
|
1206
|
+
}
|
|
1207
|
+
return lead;
|
|
1208
|
+
},
|
|
1209
|
+
|
|
1210
|
+
async formLeads(pageToken, formId, sinceSec, untilSec) {
|
|
1211
|
+
const { items, usage, complete } = await pages(
|
|
1212
|
+
url(`${formId}/leads`, {
|
|
1213
|
+
fields: LEAD_FIELDS,
|
|
1214
|
+
filtering: JSON.stringify([
|
|
1215
|
+
{
|
|
1216
|
+
field: "time_created",
|
|
1217
|
+
operator: "GREATER_THAN",
|
|
1218
|
+
value: Math.max(Math.floor(sinceSec), 0),
|
|
1219
|
+
},
|
|
1220
|
+
...(untilSec === undefined
|
|
1221
|
+
? []
|
|
1222
|
+
: [
|
|
1223
|
+
{
|
|
1224
|
+
field: "time_created",
|
|
1225
|
+
operator: "LESS_THAN",
|
|
1226
|
+
value: Math.max(Math.floor(untilSec), 0),
|
|
1227
|
+
},
|
|
1228
|
+
]),
|
|
1229
|
+
]),
|
|
1230
|
+
limit: String(PAGE_LIMIT),
|
|
1231
|
+
}),
|
|
1232
|
+
pageToken,
|
|
1233
|
+
mapLead,
|
|
1234
|
+
MAX_FORM_LEADS,
|
|
1235
|
+
);
|
|
1236
|
+
return { leads: items, usage, complete };
|
|
1237
|
+
},
|
|
911
1238
|
};
|
|
912
1239
|
}
|
|
913
1240
|
|