Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add active user tag to Mailchimp #137

Merged
merged 2 commits into from
Feb 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/core/src/data/options/defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export default {
"footer-twitter-link-url": "",
"email-templates": "{}",
"newsletter-active-member-tag": "Active member",
"newsletter-active-user-tag": "Active user",
"newsletter-default-status": NewsletterStatus.None,
"newsletter-groups": "[]",
"newsletter-resync-status": "",
Expand Down
4 changes: 3 additions & 1 deletion packages/core/src/lib/mailchimp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ export function mcMemberToNlContact(member: MCMember): NewsletterContact {
const activeMemberTag = OptionsService.getText(
"newsletter-active-member-tag"
);
const activeUserTag = OptionsService.getText("newsletter-active-user-tag");
return {
email: normalizeEmailAddress(member.email_address),
firstname: FNAME || "",
Expand All @@ -248,6 +249,7 @@ export function mcMemberToNlContact(member: MCMember): NewsletterContact {
tags: member.tags.map((tag) => tag.name),
fields,
isActiveMember:
member.tags.findIndex((t) => t.name === activeMemberTag) !== -1
member.tags.findIndex((t) => t.name === activeMemberTag) !== -1,
isActiveUser: member.tags.findIndex((t) => t.name === activeUserTag) !== -1
};
}
56 changes: 38 additions & 18 deletions packages/core/src/providers/newsletter/MailchimpProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,26 +180,50 @@ export default class MailchimpProvider implements NewsletterProvider {
contact: UpdateNewsletterContact,
oldEmail = contact.email
): Promise<NewsletterContact> {
log.info("Upsert contact " + contact.email);

const updatedContact = await this.upsertContactOrTryPending(
contact,
oldEmail
);

// Add/remove the active member tag if the statuses don't match
if (updatedContact.isActiveMember !== contact.isActiveMember) {
const tag = OptionsService.getText("newsletter-active-member-tag");
if (contact.isActiveMember) {
log.info(`Adding active member tag for ${contact.email}`);
await this.addTagToContacts([updatedContact.email], tag);
} else {
log.info(`Removing active member tag for ${contact.email}`);
await this.removeTagFromContacts([updatedContact.email], tag);
}
if (
updatedContact.isActiveMember !== contact.isActiveMember ||
updatedContact.isActiveUser !== contact.isActiveUser
) {
this.updateContactTags(updatedContact.email, {
[OptionsService.getText("newsletter-active-member-tag")]:
contact.isActiveMember,
[OptionsService.getText("newsletter-active-user-tag")]:
contact.isActiveUser
});
}

return updatedContact;
}

/**
* Update a contact with the given tags. This will overwrite any existing tags
* but will not remove any tags that are not provided.
*
* @param email The email address of the contact
* @param tags The tags to add or remove
*/
async updateContactTags(
email: string,
tags: Record<string, boolean>
): Promise<void> {
log.info("Update tags for " + email, { tags });

await this.api.instance.post(getMCMemberUrl(this.listId, email) + "/tags", {
tags: Object.entries(tags).map(([name, active]) => ({
name,
status: active ? "active" : "inactive"
}))
});
}

/**
* Update a contact with the given fields. This will overwrite any existing fields
* but will not remove any fields that are not provided.
Expand All @@ -211,15 +235,11 @@ export default class MailchimpProvider implements NewsletterProvider {
email: string,
fields: Record<string, string>
): Promise<void> {
await this.api.dispatchOperations([
{
method: "PATCH",
path: getMCMemberUrl(this.listId, email),
params: { skip_merge_validation: "true" },
body: JSON.stringify({ merge_fields: fields }),
operation_id: `update_fields_${email}`
}
]);
await this.api.instance.patch(
getMCMemberUrl(this.listId, email),
{ merge_fields: fields },
{ params: { skip_merge_validation: "true" } }
);
}

/**
Expand Down
4 changes: 4 additions & 0 deletions packages/core/src/providers/newsletter/NoneProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,8 @@ export default class NoneProvider implements NewsletterProvider {
email: string,
fields: Record<string, string>
): Promise<void> {}
async updateContactTags(
email: string,
tags: Record<string, boolean>
): Promise<void> {}
}
3 changes: 2 additions & 1 deletion packages/core/src/services/NewsletterService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ async function contactToNlUpdate(
C_MNTHAMT: contact.contributionMonthlyAmount?.toFixed(2) || "",
C_PERIOD: contact.contributionPeriod || ""
},
isActiveMember: contact.membership?.isActive || false
isActiveMember: contact.membership?.isActive || false,
isActiveUser: !!contact.password.hash
};
}

Expand Down
4 changes: 4 additions & 0 deletions packages/core/src/type/newsletter-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ export interface NewsletterProvider {
contact: UpdateNewsletterContact,
oldEmail?: string
): Promise<NewsletterContact>;
updateContactTags(
email: string,
tags: Record<string, boolean>
): Promise<void>;
updateContactFields(
email: string,
fields: Record<string, string>
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/type/update-newsletter-contact.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ export interface UpdateNewsletterContact {
groups: string[];
fields: Record<string, string>;
isActiveMember: boolean;
isActiveUser: boolean;
}