waha-shared 1.0.528 → 1.0.530
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/dist/data/bibleAudios/bibleAudios.json +22 -14
- package/dist/data/bibleTexts/bibleTexts.json +35 -32
- package/dist/data/languages/index.d.ts +1 -0
- package/dist/data/languages/languages.json +23 -0
- package/dist/data/languages/languages.schema.json +5 -0
- package/dist/data/languages/languages.zod.d.ts +12 -0
- package/dist/data/languages/languages.zod.js +1 -0
- package/dist/data/mediaDurations/mediaDurations.json +17 -0
- package/dist/data/releaseNotes/releaseNotes.json +51 -2
- package/dist/data/translationsApp/index.d.ts +1 -0
- package/dist/data/translationsApp/translationsApp.json +45 -0
- package/dist/data/translationsApp/translationsApp.schema.json +1 -0
- package/dist/data/translationsApp/translationsApp.zod.d.ts +2 -0
- package/dist/data/translationsApp/translationsApp.zod.js +1 -0
- package/dist/data/translationsIntroduction/translationsIntroduction.json +42 -0
- package/dist/data/translationsSet/translationsSet.json +1 -0
- package/dist/functions/resend.d.ts +153 -0
- package/dist/functions/resend.js +329 -0
- package/dist/functions/utils.d.ts +1 -0
- package/dist/functions/utils.js +4 -0
- package/dist/types/dripSequence.d.ts +1 -5
- package/dist/types/dripSequence.js +1 -1
- package/dist/types/languages.d.ts +6 -0
- package/dist/types/logs.d.ts +2 -2
- package/dist/types/resend.d.ts +60 -0
- package/dist/types/resend.js +69 -0
- package/dist/types/users.d.ts +6 -6
- package/dist/types/users.js +5 -1
- package/package.json +4 -3
- package/dist/functions/activeCampaign.d.ts +0 -26
- package/dist/functions/activeCampaign.js +0 -119
|
@@ -12,6 +12,6 @@ exports.DRIP_LANGUAGES = ['eng', 'ara', 'swe'];
|
|
|
12
12
|
exports.DripLanguageId = zod_1.default.enum(exports.DRIP_LANGUAGES);
|
|
13
13
|
exports.SubscribeDripBody = zod_1.default.object({
|
|
14
14
|
sequence: exports.DripSequenceId,
|
|
15
|
-
languageId:
|
|
15
|
+
languageId: zod_1.default.string(),
|
|
16
16
|
firstName: zod_1.default.string().optional(),
|
|
17
17
|
});
|
|
@@ -55,6 +55,12 @@ export declare const LanguageInfoStatuses: z.ZodObject<{
|
|
|
55
55
|
testing: "testing";
|
|
56
56
|
launched: "launched";
|
|
57
57
|
}>;
|
|
58
|
+
drip: z.ZodOptional<z.ZodEnum<{
|
|
59
|
+
notStarted: "notStarted";
|
|
60
|
+
inProgress: "inProgress";
|
|
61
|
+
testing: "testing";
|
|
62
|
+
launched: "launched";
|
|
63
|
+
}>>;
|
|
58
64
|
dmCourse: z.ZodEnum<{
|
|
59
65
|
notStarted: "notStarted";
|
|
60
66
|
inProgress: "inProgress";
|
package/dist/types/logs.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import z from 'zod';
|
|
2
2
|
export declare const WahaShareContent: z.ZodEnum<{
|
|
3
3
|
lesson: "lesson";
|
|
4
|
-
note: "note";
|
|
5
4
|
lessonLink: "lessonLink";
|
|
6
5
|
setLink: "setLink";
|
|
7
6
|
app: "app";
|
|
@@ -21,6 +20,7 @@ export declare const WahaShareContent: z.ZodEnum<{
|
|
|
21
20
|
scriptureAudio: "scriptureAudio";
|
|
22
21
|
inviteGroup: "inviteGroup";
|
|
23
22
|
offlineVersionLink: "offlineVersionLink";
|
|
23
|
+
note: "note";
|
|
24
24
|
customSet: "customSet";
|
|
25
25
|
microLesson: "microLesson";
|
|
26
26
|
}>;
|
|
@@ -29,7 +29,6 @@ export declare const ShareLogEvent: z.ZodObject<{
|
|
|
29
29
|
time: z.ZodNumber;
|
|
30
30
|
content: z.ZodEnum<{
|
|
31
31
|
lesson: "lesson";
|
|
32
|
-
note: "note";
|
|
33
32
|
lessonLink: "lessonLink";
|
|
34
33
|
setLink: "setLink";
|
|
35
34
|
app: "app";
|
|
@@ -49,6 +48,7 @@ export declare const ShareLogEvent: z.ZodObject<{
|
|
|
49
48
|
scriptureAudio: "scriptureAudio";
|
|
50
49
|
inviteGroup: "inviteGroup";
|
|
51
50
|
offlineVersionLink: "offlineVersionLink";
|
|
51
|
+
note: "note";
|
|
52
52
|
customSet: "customSet";
|
|
53
53
|
microLesson: "microLesson";
|
|
54
54
|
}>;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import z from 'zod';
|
|
2
|
+
import type { Status } from '../data/languages/languages.zod';
|
|
3
|
+
/** The two onboarding drip audiences a new signup can be enrolled in. */
|
|
4
|
+
export declare const DripSequenceId: z.ZodEnum<{
|
|
5
|
+
drip_believer: "drip_believer";
|
|
6
|
+
drip_seeker: "drip_seeker";
|
|
7
|
+
}>;
|
|
8
|
+
export type DripSequenceId = z.infer<typeof DripSequenceId>;
|
|
9
|
+
/** Ids of languages whose `status.drip` in languages.json is one of these. */
|
|
10
|
+
export declare function dripLanguagesWithStatus(...statuses: Status[]): string[];
|
|
11
|
+
/**
|
|
12
|
+
* Languages the onboarding drip is live in: provisioned in Resend and enrolled
|
|
13
|
+
* into on signup. Driven by `status.drip` in languages.json.
|
|
14
|
+
*/
|
|
15
|
+
export declare const DRIP_LANGUAGES: string[];
|
|
16
|
+
export declare const DripLanguageId: z.ZodString;
|
|
17
|
+
export type DripLanguageId = z.infer<typeof DripLanguageId>;
|
|
18
|
+
/**
|
|
19
|
+
* A single drip email's id: its sequence's id plus its position. The same as
|
|
20
|
+
* the file name for the email's source .md file.
|
|
21
|
+
*/
|
|
22
|
+
export type DripEmailId = `${DripSequenceId}_${number}`;
|
|
23
|
+
/** `POST /api/submit-subscribe` (form data): an explicit newsletter signup. */
|
|
24
|
+
export declare const NewsletterSignupBody: z.ZodObject<{
|
|
25
|
+
email: z.ZodPipe<z.ZodString, z.ZodEmail>;
|
|
26
|
+
name: z.ZodOptional<z.ZodString>;
|
|
27
|
+
}, z.core.$strip>;
|
|
28
|
+
export type NewsletterSignupBody = z.infer<typeof NewsletterSignupBody>;
|
|
29
|
+
/** `POST /api/submit-contact` (form data): the website's contact form. */
|
|
30
|
+
export declare const ContactFormBody: z.ZodObject<{
|
|
31
|
+
email: z.ZodPipe<z.ZodString, z.ZodEmail>;
|
|
32
|
+
name: z.ZodString;
|
|
33
|
+
message: z.ZodString;
|
|
34
|
+
}, z.core.$strip>;
|
|
35
|
+
export type ContactFormBody = z.infer<typeof ContactFormBody>;
|
|
36
|
+
/**
|
|
37
|
+
* `POST /api/add-to-donation-sequence` (JSON): starts the donor thank-you
|
|
38
|
+
* drip.
|
|
39
|
+
*/
|
|
40
|
+
export declare const DonorSequenceBody: z.ZodObject<{
|
|
41
|
+
email: z.ZodPipe<z.ZodString, z.ZodEmail>;
|
|
42
|
+
name: z.ZodOptional<z.ZodString>;
|
|
43
|
+
newsletter: z.ZodOptional<z.ZodBoolean>;
|
|
44
|
+
}, z.core.$strip>;
|
|
45
|
+
export type DonorSequenceBody = z.infer<typeof DonorSequenceBody>;
|
|
46
|
+
/**
|
|
47
|
+
* `POST /api/subscribe-drip` (JSON, Firebase ID token). Any language is
|
|
48
|
+
* accepted so callers don't need to know which ones have a drip; unsupported
|
|
49
|
+
* ones are a no-op.
|
|
50
|
+
*/
|
|
51
|
+
export declare const SubscribeDripBody: z.ZodObject<{
|
|
52
|
+
sequence: z.ZodEnum<{
|
|
53
|
+
drip_believer: "drip_believer";
|
|
54
|
+
drip_seeker: "drip_seeker";
|
|
55
|
+
}>;
|
|
56
|
+
languageId: z.ZodString;
|
|
57
|
+
firstName: z.ZodOptional<z.ZodString>;
|
|
58
|
+
newsletter: z.ZodOptional<z.ZodBoolean>;
|
|
59
|
+
}, z.core.$strip>;
|
|
60
|
+
export type SubscribeDripBody = z.infer<typeof SubscribeDripBody>;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.SubscribeDripBody = exports.DonorSequenceBody = exports.ContactFormBody = exports.NewsletterSignupBody = exports.DripLanguageId = exports.DRIP_LANGUAGES = exports.DripSequenceId = void 0;
|
|
7
|
+
exports.dripLanguagesWithStatus = dripLanguagesWithStatus;
|
|
8
|
+
const zod_1 = __importDefault(require("zod"));
|
|
9
|
+
const languages_1 = require("../data/languages");
|
|
10
|
+
/** The two onboarding drip audiences a new signup can be enrolled in. */
|
|
11
|
+
exports.DripSequenceId = zod_1.default.enum(['drip_believer', 'drip_seeker']);
|
|
12
|
+
/** Ids of languages whose `status.drip` in languages.json is one of these. */
|
|
13
|
+
function dripLanguagesWithStatus(...statuses) {
|
|
14
|
+
return languages_1.languages
|
|
15
|
+
.filter((language) => statuses.includes(language.status.drip ?? 'notStarted'))
|
|
16
|
+
.map((language) => language.languageId);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Languages the onboarding drip is live in: provisioned in Resend and enrolled
|
|
20
|
+
* into on signup. Driven by `status.drip` in languages.json.
|
|
21
|
+
*/
|
|
22
|
+
exports.DRIP_LANGUAGES = dripLanguagesWithStatus('launched');
|
|
23
|
+
exports.DripLanguageId = zod_1.default
|
|
24
|
+
.string()
|
|
25
|
+
.refine((id) => exports.DRIP_LANGUAGES.includes(id), 'Not a launched drip language.');
|
|
26
|
+
/*
|
|
27
|
+
* Request bodies for the website endpoints that write to Resend. The website
|
|
28
|
+
* parses incoming requests with these, and the app types what it sends with
|
|
29
|
+
* them. Auth (a reCAPTCHA token or a Firebase ID token) isn't part of the body
|
|
30
|
+
* schemas; each endpoint checks it separately.
|
|
31
|
+
*/
|
|
32
|
+
const Email = zod_1.default.string().trim().pipe(zod_1.default.email());
|
|
33
|
+
const Name = zod_1.default.string().trim().max(200);
|
|
34
|
+
/**
|
|
35
|
+
* Whether the user ticked the newsletter box alongside this signup. Shipped app
|
|
36
|
+
* versions don't send it, and missing means no: the newsletter needs consent.
|
|
37
|
+
*/
|
|
38
|
+
const Newsletter = zod_1.default.boolean().optional();
|
|
39
|
+
/** `POST /api/submit-subscribe` (form data): an explicit newsletter signup. */
|
|
40
|
+
exports.NewsletterSignupBody = zod_1.default.object({
|
|
41
|
+
email: Email,
|
|
42
|
+
name: Name.optional(),
|
|
43
|
+
});
|
|
44
|
+
/** `POST /api/submit-contact` (form data): the website's contact form. */
|
|
45
|
+
exports.ContactFormBody = zod_1.default.object({
|
|
46
|
+
email: Email,
|
|
47
|
+
name: Name,
|
|
48
|
+
message: zod_1.default.string().trim().min(1).max(5000),
|
|
49
|
+
});
|
|
50
|
+
/**
|
|
51
|
+
* `POST /api/add-to-donation-sequence` (JSON): starts the donor thank-you
|
|
52
|
+
* drip.
|
|
53
|
+
*/
|
|
54
|
+
exports.DonorSequenceBody = zod_1.default.object({
|
|
55
|
+
email: Email,
|
|
56
|
+
name: Name.optional(),
|
|
57
|
+
newsletter: Newsletter,
|
|
58
|
+
});
|
|
59
|
+
/**
|
|
60
|
+
* `POST /api/subscribe-drip` (JSON, Firebase ID token). Any language is
|
|
61
|
+
* accepted so callers don't need to know which ones have a drip; unsupported
|
|
62
|
+
* ones are a no-op.
|
|
63
|
+
*/
|
|
64
|
+
exports.SubscribeDripBody = zod_1.default.object({
|
|
65
|
+
sequence: exports.DripSequenceId,
|
|
66
|
+
languageId: zod_1.default.string(),
|
|
67
|
+
firstName: Name.optional(),
|
|
68
|
+
newsletter: Newsletter,
|
|
69
|
+
});
|
package/dist/types/users.d.ts
CHANGED
|
@@ -17,7 +17,6 @@ export declare const SharedUserReduxState: z.ZodObject<{
|
|
|
17
17
|
}, z.core.$strip>>;
|
|
18
18
|
phoneNumber: z.ZodOptional<z.ZodString>;
|
|
19
19
|
isInAGroup: z.ZodOptional<z.ZodBoolean>;
|
|
20
|
-
isSubscribedToEmailUpdates: z.ZodOptional<z.ZodBoolean>;
|
|
21
20
|
hasDonated: z.ZodOptional<z.ZodBoolean>;
|
|
22
21
|
}, z.core.$strip>;
|
|
23
22
|
/**
|
|
@@ -33,6 +32,7 @@ export declare const DeviceUserReduxState: z.ZodObject<{
|
|
|
33
32
|
engagementScore: z.ZodOptional<z.ZodNumber>;
|
|
34
33
|
finishedOnboarding: z.ZodOptional<z.ZodNumber>;
|
|
35
34
|
firstOpen: z.ZodOptional<z.ZodNumber>;
|
|
35
|
+
newsletterOptIn: z.ZodOptional<z.ZodBoolean>;
|
|
36
36
|
}, z.core.$strip>;
|
|
37
37
|
export declare const UserReduxState: z.ZodObject<{
|
|
38
38
|
pastLocations: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
@@ -44,6 +44,7 @@ export declare const UserReduxState: z.ZodObject<{
|
|
|
44
44
|
engagementScore: z.ZodOptional<z.ZodNumber>;
|
|
45
45
|
finishedOnboarding: z.ZodOptional<z.ZodNumber>;
|
|
46
46
|
firstOpen: z.ZodOptional<z.ZodNumber>;
|
|
47
|
+
newsletterOptIn: z.ZodOptional<z.ZodBoolean>;
|
|
47
48
|
email: z.ZodOptional<z.ZodEmail>;
|
|
48
49
|
location: z.ZodOptional<z.ZodObject<{
|
|
49
50
|
city: z.ZodOptional<z.ZodString>;
|
|
@@ -52,7 +53,6 @@ export declare const UserReduxState: z.ZodObject<{
|
|
|
52
53
|
}, z.core.$strip>>;
|
|
53
54
|
phoneNumber: z.ZodOptional<z.ZodString>;
|
|
54
55
|
isInAGroup: z.ZodOptional<z.ZodBoolean>;
|
|
55
|
-
isSubscribedToEmailUpdates: z.ZodOptional<z.ZodBoolean>;
|
|
56
56
|
hasDonated: z.ZodOptional<z.ZodBoolean>;
|
|
57
57
|
}, z.core.$strip>;
|
|
58
58
|
export type UserReduxState = z.infer<typeof UserReduxState>;
|
|
@@ -79,6 +79,7 @@ export declare const DeviceInfo: z.ZodObject<{
|
|
|
79
79
|
engagementScore: z.ZodOptional<z.ZodNumber>;
|
|
80
80
|
finishedOnboarding: z.ZodOptional<z.ZodNumber>;
|
|
81
81
|
firstOpen: z.ZodOptional<z.ZodNumber>;
|
|
82
|
+
newsletterOptIn: z.ZodOptional<z.ZodBoolean>;
|
|
82
83
|
phoneLocation: z.ZodOptional<z.ZodString>;
|
|
83
84
|
pushToken: z.ZodOptional<z.ZodString>;
|
|
84
85
|
notificationPermission: z.ZodOptional<z.ZodEnum<{
|
|
@@ -107,6 +108,7 @@ export declare const DeviceInfoFields: {
|
|
|
107
108
|
engagementScore: "engagementScore";
|
|
108
109
|
finishedOnboarding: "finishedOnboarding";
|
|
109
110
|
firstOpen: "firstOpen";
|
|
111
|
+
newsletterOptIn: "newsletterOptIn";
|
|
110
112
|
phoneLocation: "phoneLocation";
|
|
111
113
|
pushToken: "pushToken";
|
|
112
114
|
notificationPermission: "notificationPermission";
|
|
@@ -128,7 +130,6 @@ export declare const WahaUser: z.ZodObject<{
|
|
|
128
130
|
}, z.core.$strip>>;
|
|
129
131
|
phoneNumber: z.ZodOptional<z.ZodString>;
|
|
130
132
|
isInAGroup: z.ZodOptional<z.ZodBoolean>;
|
|
131
|
-
isSubscribedToEmailUpdates: z.ZodOptional<z.ZodBoolean>;
|
|
132
133
|
hasDonated: z.ZodOptional<z.ZodBoolean>;
|
|
133
134
|
wahaUserId: z.ZodString;
|
|
134
135
|
name: z.ZodOptional<z.ZodString>;
|
|
@@ -151,7 +152,6 @@ export declare const WahaUserFields: {
|
|
|
151
152
|
location: "location";
|
|
152
153
|
phoneNumber: "phoneNumber";
|
|
153
154
|
isInAGroup: "isInAGroup";
|
|
154
|
-
isSubscribedToEmailUpdates: "isSubscribedToEmailUpdates";
|
|
155
155
|
hasDonated: "hasDonated";
|
|
156
156
|
lastUpdate: "lastUpdate";
|
|
157
157
|
hasAccount: "hasAccount";
|
|
@@ -244,7 +244,6 @@ export declare const LegacyWahaUser: z.ZodObject<{
|
|
|
244
244
|
time: z.ZodNumber;
|
|
245
245
|
content: z.ZodEnum<{
|
|
246
246
|
lesson: "lesson";
|
|
247
|
-
note: "note";
|
|
248
247
|
lessonLink: "lessonLink";
|
|
249
248
|
setLink: "setLink";
|
|
250
249
|
app: "app";
|
|
@@ -264,6 +263,7 @@ export declare const LegacyWahaUser: z.ZodObject<{
|
|
|
264
263
|
scriptureAudio: "scriptureAudio";
|
|
265
264
|
inviteGroup: "inviteGroup";
|
|
266
265
|
offlineVersionLink: "offlineVersionLink";
|
|
266
|
+
note: "note";
|
|
267
267
|
customSet: "customSet";
|
|
268
268
|
microLesson: "microLesson";
|
|
269
269
|
}>;
|
|
@@ -318,7 +318,6 @@ export declare const LegacyWahaUserFields: {
|
|
|
318
318
|
location: "location";
|
|
319
319
|
phoneNumber: "phoneNumber";
|
|
320
320
|
isInAGroup: "isInAGroup";
|
|
321
|
-
isSubscribedToEmailUpdates: "isSubscribedToEmailUpdates";
|
|
322
321
|
hasDonated: "hasDonated";
|
|
323
322
|
pastLocations: "pastLocations";
|
|
324
323
|
utm: "utm";
|
|
@@ -337,6 +336,7 @@ export declare const LegacyWahaUserFields: {
|
|
|
337
336
|
sharingScore: "sharingScore";
|
|
338
337
|
isAdvocate: "isAdvocate";
|
|
339
338
|
isCCWorker: "isCCWorker";
|
|
339
|
+
isSubscribedToEmailUpdates: "isSubscribedToEmailUpdates";
|
|
340
340
|
howFamiliarWithBible: "howFamiliarWithBible";
|
|
341
341
|
followingFor2Years: "followingFor2Years";
|
|
342
342
|
notificationsClicked: "notificationsClicked";
|
package/dist/types/users.js
CHANGED
|
@@ -22,7 +22,6 @@ exports.SharedUserReduxState = zod_1.default
|
|
|
22
22
|
location: location_1.WahaUserLocation,
|
|
23
23
|
phoneNumber: zod_1.default.string(),
|
|
24
24
|
isInAGroup: zod_1.default.boolean(),
|
|
25
|
-
isSubscribedToEmailUpdates: zod_1.default.boolean(),
|
|
26
25
|
hasDonated: zod_1.default.boolean(),
|
|
27
26
|
})
|
|
28
27
|
.partial();
|
|
@@ -36,6 +35,11 @@ exports.DeviceUserReduxState = zod_1.default
|
|
|
36
35
|
engagementScore: zod_1.default.number(),
|
|
37
36
|
finishedOnboarding: zod_1.default.number(),
|
|
38
37
|
firstOpen: zod_1.default.number(),
|
|
38
|
+
/**
|
|
39
|
+
* Whether the newsletter box was ticked on the latest sign-in attempt.
|
|
40
|
+
* Kept here so the choice survives the trip out to a magic link and back.
|
|
41
|
+
*/
|
|
42
|
+
newsletterOptIn: zod_1.default.boolean(),
|
|
39
43
|
})
|
|
40
44
|
.partial();
|
|
41
45
|
exports.UserReduxState = zod_1.default.object({
|
package/package.json
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "waha-shared",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.530",
|
|
4
4
|
"author": "Waha",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@types/signale": "^1.4.7",
|
|
7
|
+
"resend": "^6.27.0",
|
|
7
8
|
"signale": "^1.4.0"
|
|
8
9
|
},
|
|
9
10
|
"peerDependencies": {
|
|
@@ -28,7 +29,7 @@
|
|
|
28
29
|
"license": "MIT",
|
|
29
30
|
"scripts": {
|
|
30
31
|
"build": "npx tsc && npx copyfiles \"data/**/*.json\" dist",
|
|
31
|
-
"update": "
|
|
32
|
-
"
|
|
32
|
+
"update": "bash publish.sh",
|
|
33
|
+
"update:preview": "bash publish.sh --preview"
|
|
33
34
|
}
|
|
34
35
|
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
declare const listNumbers: {
|
|
2
|
-
newsletter: number;
|
|
3
|
-
volunteer: number;
|
|
4
|
-
'dm-course': number;
|
|
5
|
-
'in-app-donation-sequence': number;
|
|
6
|
-
};
|
|
7
|
-
/**
|
|
8
|
-
* The ActiveCampaign API URL that looks up a contact by email. The email is
|
|
9
|
-
* encoded, since a raw `+` in a query string reads as a space and
|
|
10
|
-
* `name+tag@x.com` would never match.
|
|
11
|
-
*/
|
|
12
|
-
export declare function contactLookupUrl(apiUrl: string, email: string): string;
|
|
13
|
-
export declare function addToACList({ baseUrl, email, list, token, name, }: {
|
|
14
|
-
token: string;
|
|
15
|
-
baseUrl: string;
|
|
16
|
-
email: string;
|
|
17
|
-
list: keyof typeof listNumbers;
|
|
18
|
-
name?: string;
|
|
19
|
-
}): Promise<void>;
|
|
20
|
-
export declare function getContactsInList({ baseUrl, limit, list, token, }: {
|
|
21
|
-
token: string;
|
|
22
|
-
baseUrl: string;
|
|
23
|
-
list: keyof typeof listNumbers;
|
|
24
|
-
limit?: number;
|
|
25
|
-
}): Promise<string[]>;
|
|
26
|
-
export {};
|
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.contactLookupUrl = contactLookupUrl;
|
|
7
|
-
exports.addToACList = addToACList;
|
|
8
|
-
exports.getContactsInList = getContactsInList;
|
|
9
|
-
const signale_1 = __importDefault(require("signale"));
|
|
10
|
-
const zod_1 = require("zod");
|
|
11
|
-
const listNumbers = {
|
|
12
|
-
newsletter: 3,
|
|
13
|
-
volunteer: 5,
|
|
14
|
-
'dm-course': 9,
|
|
15
|
-
'in-app-donation-sequence': 17,
|
|
16
|
-
};
|
|
17
|
-
/**
|
|
18
|
-
* The ActiveCampaign API URL that looks up a contact by email. The email is
|
|
19
|
-
* encoded, since a raw `+` in a query string reads as a space and
|
|
20
|
-
* `name+tag@x.com` would never match.
|
|
21
|
-
*/
|
|
22
|
-
function contactLookupUrl(apiUrl, email) {
|
|
23
|
-
return `${apiUrl}/contacts?email=${encodeURIComponent(email)}`;
|
|
24
|
-
}
|
|
25
|
-
/** The part of an ActiveCampaign list-contacts response that's read. */
|
|
26
|
-
const ContactsPage = zod_1.z.object({
|
|
27
|
-
contacts: zod_1.z.array(zod_1.z.object({ id: zod_1.z.string(), email: zod_1.z.string() })).optional(),
|
|
28
|
-
});
|
|
29
|
-
/** The part of an ActiveCampaign contact lookup response that's read. */
|
|
30
|
-
const ContactLookup = zod_1.z.object({
|
|
31
|
-
contacts: zod_1.z.array(zod_1.z.object({ id: zod_1.z.string() })),
|
|
32
|
-
});
|
|
33
|
-
/** The part of an ActiveCampaign create-contact response that's read. */
|
|
34
|
-
const CreatedContact = zod_1.z.object({ contact: zod_1.z.object({ id: zod_1.z.string() }) });
|
|
35
|
-
async function addToACList({ baseUrl, email, list, token, name, }) {
|
|
36
|
-
const headers = { 'Api-Token': token, 'Content-Type': 'application/json' };
|
|
37
|
-
const url = `${baseUrl}/api/3`;
|
|
38
|
-
const findContact = await fetch(contactLookupUrl(url, email), {
|
|
39
|
-
method: 'GET',
|
|
40
|
-
headers,
|
|
41
|
-
});
|
|
42
|
-
const findContactJson = await findContact.json();
|
|
43
|
-
if (!findContact.ok) {
|
|
44
|
-
throw new Error(`Failed to retrieve contact with ${email}. ${JSON.stringify(findContactJson)}`);
|
|
45
|
-
}
|
|
46
|
-
const [existingContact] = ContactLookup.parse(findContactJson).contacts;
|
|
47
|
-
let contactId;
|
|
48
|
-
if (existingContact) {
|
|
49
|
-
contactId = existingContact.id;
|
|
50
|
-
}
|
|
51
|
-
else {
|
|
52
|
-
const createContact = await fetch(`${url}/contacts`, {
|
|
53
|
-
method: 'POST',
|
|
54
|
-
headers,
|
|
55
|
-
body: JSON.stringify({
|
|
56
|
-
contact: { email, name },
|
|
57
|
-
}),
|
|
58
|
-
});
|
|
59
|
-
const createContactJson = await createContact.json();
|
|
60
|
-
if (!createContact.ok) {
|
|
61
|
-
throw new Error(`Failed to create new contact with ${email}. ${JSON.stringify(createContactJson)}`);
|
|
62
|
-
}
|
|
63
|
-
signale_1.default.success(`Created new AC contact with email ${email}.`);
|
|
64
|
-
contactId = CreatedContact.parse(createContactJson).contact.id;
|
|
65
|
-
}
|
|
66
|
-
const subscribe = await fetch(`${url}/contactLists`, {
|
|
67
|
-
method: 'POST',
|
|
68
|
-
headers: { 'Api-Token': token, 'Content-Type': 'application/json' },
|
|
69
|
-
body: JSON.stringify({
|
|
70
|
-
contactList: {
|
|
71
|
-
list: listNumbers[list],
|
|
72
|
-
status: 1,
|
|
73
|
-
contact: contactId,
|
|
74
|
-
},
|
|
75
|
-
}),
|
|
76
|
-
});
|
|
77
|
-
const subscribeJson = await subscribe.json();
|
|
78
|
-
if (!subscribe.ok) {
|
|
79
|
-
throw new Error(`Failed to add contact to list ${list}. Response: ${JSON.stringify(subscribeJson)}`);
|
|
80
|
-
}
|
|
81
|
-
signale_1.default.success(`${email} added to AC list ${list}.`);
|
|
82
|
-
}
|
|
83
|
-
async function getContactsInList({ baseUrl, limit = 100, list, token, }) {
|
|
84
|
-
const headers = { 'Api-Token': token, 'Content-Type': 'application/json' };
|
|
85
|
-
const url = `${baseUrl}/api/3`;
|
|
86
|
-
const allEmails = [];
|
|
87
|
-
let lastContactId = null;
|
|
88
|
-
let hasMoreContacts = true;
|
|
89
|
-
while (hasMoreContacts) {
|
|
90
|
-
let requestUrl = `${url}/contacts?listid=${listNumbers[list]}&orders[id]=ASC&limit=${limit}`;
|
|
91
|
-
if (lastContactId) {
|
|
92
|
-
requestUrl += `&id_greater=${lastContactId}`;
|
|
93
|
-
}
|
|
94
|
-
const response = await fetch(requestUrl, {
|
|
95
|
-
method: 'GET',
|
|
96
|
-
headers,
|
|
97
|
-
});
|
|
98
|
-
const data = await response.json();
|
|
99
|
-
if (!response.ok) {
|
|
100
|
-
throw new Error(`Failed to retrieve contacts in list ${list}. ${JSON.stringify(data)}`);
|
|
101
|
-
}
|
|
102
|
-
const contacts = ContactsPage.parse(data).contacts || [];
|
|
103
|
-
if (contacts.length === 0) {
|
|
104
|
-
hasMoreContacts = false;
|
|
105
|
-
}
|
|
106
|
-
else {
|
|
107
|
-
// Add emails from this batch
|
|
108
|
-
const emails = contacts.map((contact) => contact.email);
|
|
109
|
-
allEmails.push(...emails);
|
|
110
|
-
// Update the last contact ID for next iteration
|
|
111
|
-
lastContactId = contacts[contacts.length - 1].id;
|
|
112
|
-
// If we got fewer contacts than the limit, we've reached the end
|
|
113
|
-
if (contacts.length < limit) {
|
|
114
|
-
hasMoreContacts = false;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
return allEmails;
|
|
119
|
-
}
|