Skip to content

Commit

Permalink
Added basic processing of Mailchimp message webhooks
Browse files Browse the repository at this point in the history
  • Loading branch information
VSydor committed Jun 19, 2023
1 parent bab7584 commit 39b708b
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public Response inboundSmsWebhook(
@Context HttpServletRequest request
) throws Exception {
Environment env = envFactory.init(request);
SecurityUtil.verifyApiKey(env);
SecurityUtil.verifyApiKey(env); //TODO: not needed?

String jobName = "SMS Inbound";
env.startJobLog(JobType.EVENT, null, jobName, "MBT");
Expand All @@ -68,7 +68,7 @@ public Response smsStatusWebhook(
@Context HttpServletRequest request
) throws Exception {
Environment env = envFactory.init(request);
SecurityUtil.verifyApiKey(env);
SecurityUtil.verifyApiKey(env); //TODO: not needed?

// String jobName = "SMS Status";
// env.startJobLog(JobType.EVENT, null, jobName, "MBT");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package com.impactupgrade.nucleus.controller;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.impactupgrade.nucleus.entity.JobType;
import com.impactupgrade.nucleus.environment.Environment;
import com.impactupgrade.nucleus.environment.EnvironmentFactory;
import org.apache.logging.log4j.LogManager;
Expand All @@ -13,15 +16,23 @@
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.util.List;
import java.util.Map;
import java.util.Set;

@Path("/mailchimp")
public class MailchimpController {

private static final Logger log = LogManager.getLogger(MailchimpController.class);
private static final Set<String> SUPPORTED_EVENT_TYPES = Set.of(
"send", "delivered", "deferral",
"hard_bounce", "soft_bounce", "open",
"click", "spam", "unsub", "reject"
);

protected final EnvironmentFactory envFactory;

public MailchimpController(EnvironmentFactory envFactory){
public MailchimpController(EnvironmentFactory envFactory) {
this.envFactory = envFactory;
}

Expand All @@ -34,7 +45,7 @@ public Response webhook(
@FormParam("email") String email,
@FormParam("list_id") String listId,
@Context HttpServletRequest request
) throws Exception{
) throws Exception {
log.info("action = {} reason = {} email = {} list_id = {}", action, reason, email, listId);

Environment env = envFactory.init(request);
Expand All @@ -46,4 +57,105 @@ public Response webhook(

return Response.status(200).build();
}

// Message events
@Path("/webhook/message")
@POST
@Consumes(MediaType.APPLICATION_JSON)
public Response messageEvent(
MessageWebhookPayload webhookPayload,
@Context HttpServletRequest request
) throws Exception {
Environment env = envFactory.init(request);
//SecurityUtil.verifyApiKey(env); //TODO: not needed?

log.info("Mailchimp message event batch received. Batch size: {}.", webhookPayload.events.size());

String jobName = "Mailchimp webhook events batch";
env.startJobLog(JobType.EVENT, null, jobName, "Mailchimp");

for (Event event: webhookPayload.events) {
try {
processEvent(event, env);
} catch (Exception e) {
log.error("Failed to process event! Event type/email: {}/{}; {}",
event.eventType, event.message.email, e);
}
}

env.endJobLog(jobName);

return Response.status(200).build();
}

private void processEvent(Event event, Environment env) throws Exception {
if (event == null) {
return;
}
if (!SUPPORTED_EVENT_TYPES.contains(event.eventType)) {
log.warn("Unsupported event type: {}! Returning...", event.eventType);
return;
}
if ("send".equalsIgnoreCase(event.eventType)) {
env.messagingService().upsertCrmConversation( //TODO: add type? sms/email?
event.message.subject,
event.message.id,
event.message.email); // TODO: decided if can/should use user id/email
// to store all emails as a single conversation?
} else {
log.info("skipping event type {}...", event.eventType);
}
}

public static final class MessageWebhookPayload {
@JsonProperty("mandrill_events")
public List<Event> events;
}

@JsonIgnoreProperties(ignoreUnknown = true)
public static final class Event {
@JsonProperty("ts")
public Long timestamp;
public String url;
public String ip;
@JsonProperty("user_agent")
public String userAgent;
//public Object location;
//@JsonProperty("user_agent_parsed")
//public List<Object> userAgentParsed;
@JsonProperty("_id")
public String id;

@JsonProperty("event")
public String eventType;
@JsonProperty("msg")
public Message message;
}

@JsonIgnoreProperties(ignoreUnknown = true)
public static final class Message {
@JsonProperty("_id")
public String id;
@JsonProperty("ts")
public Long timestamp;
public String email;
public String sender;
public String subject;
@JsonProperty("smtp_events")
public List<Object> smtpEvents;
public List<Object> opens; //TODO: add object definition if needed;
public List<Object> clicks;
public List<String> tags;
public Map<String, Object> metadata;
public String state; // One of: sent, rejected, spam, unsub, bounced, or soft-bounced
@JsonProperty("subaccount")
public String subAccount;
public String diag; //specific SMTP response code and bounce description, if any, received from the remote server
@JsonProperty("bounce_description")
public String bounceDescription;
public String template;
}

//TODO: Sync events (add/remove (to either of allowlist or denylist))
//TODO: inbound messages
}
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,8 @@ public void upsertCrmConversation(String messageBody, String messageSid, String
subject = "SMS MESSAGE: " + messageSid;
}

Optional<CrmTask> _crmTask = env.primaryCrmService().getTaskBySubject(subject);
CrmService primaryCrmService = env.primaryCrmService();
Optional<CrmTask> _crmTask = primaryCrmService.getTaskBySubject(subject);
CrmTask crmTask;

if (_crmTask.isEmpty()) {
Expand Down

0 comments on commit 39b708b

Please sign in to comment.