/// <summary> /// Sends the specified rock message. /// </summary> /// <param name="rockMessage">The rock message.</param> /// <param name="mediumEntityTypeId">The medium entity type identifier.</param> /// <param name="mediumAttributes">The medium attributes.</param> /// <param name="errorMessages">The error messages.</param> /// <returns></returns> public override bool Send(RockMessage rockMessage, int mediumEntityTypeId, Dictionary <string, string> mediumAttributes, out List <string> errorMessages) { errorMessages = new List <string>(); var pushMessage = rockMessage as RockPushMessage; if (pushMessage != null) { // Get server key string serverKey = GetAttributeValue("ServerKey"); var sender = new Sender(serverKey); // Common Merge Field var mergeFields = Lava.LavaHelper.GetCommonMergeFields(null, rockMessage.CurrentPerson); foreach (var mergeField in rockMessage.AdditionalMergeFields) { mergeFields.AddOrReplace(mergeField.Key, mergeField.Value); } var recipients = rockMessage.GetRecipients(); if (pushMessage.SendSeperatelyToEachRecipient) { foreach (var recipient in recipients) { try { foreach (var mergeField in mergeFields) { recipient.MergeFields.AddOrIgnore(mergeField.Key, mergeField.Value); } PushMessage(sender, new List <string> { recipient.To }, pushMessage, recipient.MergeFields); } catch (Exception ex) { errorMessages.Add(ex.Message); ExceptionLogService.LogException(ex, null); } } } else { try { PushMessage(sender, recipients.Select(r => r.To).ToList(), pushMessage, mergeFields); } catch (Exception ex) { errorMessages.Add(ex.Message); ExceptionLogService.LogException(ex, null); } } } return(!errorMessages.Any()); }
/// <summary> /// Sends the specified rock message. /// </summary> /// <param name="rockMessage">The rock message.</param> /// <param name="mediumEntityTypeId">The medium entity type identifier.</param> /// <param name="mediumAttributes">The medium attributes.</param> /// <param name="errorMessages">The error messages.</param> /// <returns></returns> public override bool Send(RockMessage rockMessage, int mediumEntityTypeId, Dictionary <string, string> mediumAttributes, out List <string> errorMessages) { errorMessages = new List <string>(); var emailMessage = rockMessage as RockEmailMessage; if (emailMessage != null) { // Common Merge Field var mergeFields = Lava.LavaHelper.GetCommonMergeFields(null, rockMessage.CurrentPerson); foreach (var mergeField in rockMessage.AdditionalMergeFields) { mergeFields.AddOrReplace(mergeField.Key, mergeField.Value); } string fromAddress = emailMessage.FromEmail; string fromName = emailMessage.FromName; // Resolve any possible merge fields in the from address fromAddress = fromAddress.ResolveMergeFields(mergeFields, emailMessage.CurrentPerson, emailMessage.EnabledLavaCommands); fromName = fromName.ResolveMergeFields(mergeFields, emailMessage.CurrentPerson, emailMessage.EnabledLavaCommands); // From - if none is set, use the one in the Organization's GlobalAttributes. var globalAttributes = GlobalAttributesCache.Get(); if (string.IsNullOrWhiteSpace(fromAddress)) { fromAddress = globalAttributes.GetValue("OrganizationEmail"); } if (string.IsNullOrWhiteSpace(fromName)) { fromName = globalAttributes.GetValue("OrganizationName"); } if (fromAddress.IsNullOrWhiteSpace()) { errorMessages.Add("A From address was not provided and no Organization email address is configured."); return(false); } MailMessage message = new MailMessage(); // Reply To try { if (emailMessage.ReplyToEmail.IsNotNullOrWhiteSpace()) { // Resolve any possible merge fields in the replyTo address message.ReplyToList.Add(new MailAddress(emailMessage.ReplyToEmail.ResolveMergeFields(mergeFields, emailMessage.CurrentPerson, emailMessage.EnabledLavaCommands))); } } catch { } message.IsBodyHtml = true; message.Priority = MailPriority.Normal; using (var smtpClient = GetSmtpClient()) { foreach (var messageRecipient in rockMessage.GetRecipients()) { try { foreach (var mergeField in mergeFields) { messageRecipient.MergeFields.AddOrIgnore(mergeField.Key, mergeField.Value); } message.To.Clear(); message.CC.Clear(); message.Bcc.Clear(); message.Headers.Clear(); // Set From/To and check safe sender message.From = new MailAddress(fromAddress, fromName); message.To.Add(new MailAddress( messageRecipient.To.ResolveMergeFields(messageRecipient.MergeFields, emailMessage.CurrentPerson, emailMessage.EnabledLavaCommands), messageRecipient.Name.ResolveMergeFields(messageRecipient.MergeFields, emailMessage.CurrentPerson, emailMessage.EnabledLavaCommands))); CheckSafeSender(message, globalAttributes); // cc foreach (string cc in emailMessage.CCEmails.Where(e => e != "")) { // Resolve any possible merge fields in the cc address string ccRecipient = cc.ResolveMergeFields(messageRecipient.MergeFields, emailMessage.CurrentPerson, emailMessage.EnabledLavaCommands); message.CC.Add(new MailAddress(ccRecipient)); } // bcc foreach (string bcc in emailMessage.BCCEmails.Where(e => e != "")) { // Resolve any possible merge fields in the cc address string bccRecipient = bcc.ResolveMergeFields(messageRecipient.MergeFields, emailMessage.CurrentPerson, emailMessage.EnabledLavaCommands); message.Bcc.Add(new MailAddress(bccRecipient)); } // Subject string subject = ResolveText(emailMessage.Subject, emailMessage.CurrentPerson, emailMessage.EnabledLavaCommands, messageRecipient.MergeFields, emailMessage.AppRoot, emailMessage.ThemeRoot); // Body string body = ResolveText(emailMessage.Message, emailMessage.CurrentPerson, emailMessage.EnabledLavaCommands, messageRecipient.MergeFields, emailMessage.AppRoot, emailMessage.ThemeRoot); body = Regex.Replace(body, @"\[\[\s*UnsubscribeOption\s*\]\]", string.Empty); message.Subject = subject.Left(998); message.Body = body; var metaData = new Dictionary <string, string>(emailMessage.MessageMetaData); // If a communication is going to get created, create a guid for tracking the opens/clicks Guid?recipientGuid = null; if (emailMessage.CreateCommunicationRecord) { recipientGuid = Guid.NewGuid(); metaData.Add("communication_recipient_guid", recipientGuid.Value.ToString()); } using (var rockContext = new RockContext()) { // Recreate the attachments message.Attachments.Clear(); if (emailMessage.Attachments.Any()) { var binaryFileService = new BinaryFileService(rockContext); foreach (var binaryFileId in emailMessage.Attachments.Where(a => a != null).Select(a => a.Id)) { var attachment = binaryFileService.Get(binaryFileId); if (attachment != null) { message.Attachments.Add(new Attachment(attachment.ContentStream, attachment.FileName)); } } } AddAdditionalHeaders(message, metaData); smtpClient.Send(message); } if (emailMessage.CreateCommunicationRecord) { var transaction = new SaveCommunicationTransaction(messageRecipient, emailMessage.FromName, emailMessage.FromEmail, subject, body); transaction.RecipientGuid = recipientGuid; RockQueue.TransactionQueue.Enqueue(transaction); } } catch (Exception ex) { errorMessages.Add(ex.Message); ExceptionLogService.LogException(ex); } } } } return(!errorMessages.Any()); }
/// <summary> /// Sends the specified rock message. /// </summary> /// <param name="rockMessage">The rock message.</param> /// <param name="mediumEntityTypeId">The medium entity type identifier. Not used.</param> /// <param name="mediumAttributes">The medium attributes. Not used.</param> /// <param name="errorMessages">The error messages.</param> /// <returns></returns> public override bool Send(RockMessage rockMessage, int mediumEntityTypeId, Dictionary <string, string> mediumAttributes, out List <string> errorMessages) { errorMessages = new List <string>(); var emailMessage = rockMessage as RockEmailMessage; if (emailMessage == null) { return(!errorMessages.Any()); } var globalAttributes = GlobalAttributesCache.Get(); // Common Merge Field var mergeFields = Lava.LavaHelper.GetCommonMergeFields(null, rockMessage.CurrentPerson); foreach (var mergeField in rockMessage.AdditionalMergeFields) { mergeFields.AddOrReplace(mergeField.Key, mergeField.Value); } // Resolve any possible merge fields in the from address string fromAddress = emailMessage.FromEmail.ResolveMergeFields(mergeFields, emailMessage.CurrentPerson, emailMessage.EnabledLavaCommands); string fromName = emailMessage.FromName.ResolveMergeFields(mergeFields, emailMessage.CurrentPerson, emailMessage.EnabledLavaCommands); // Replace blank values with organizational defaults fromAddress = fromAddress.IsNullOrWhiteSpace() ? globalAttributes.GetValue("OrganizationEmail") : fromAddress; fromName = fromName.IsNullOrWhiteSpace() ? globalAttributes.GetValue("OrganizationName") : fromName; if (!fromAddress.IsValidEmail()) { errorMessages.Add("A From address was not provided."); return(false); } RestRequest restRequest = null; foreach (var rockMessageRecipient in rockMessage.GetRecipients()) { try { restRequest = new RestRequest(GetAttributeValue("Resource"), Method.POST); restRequest.AddParameter("domian", GetAttributeValue("Domain"), ParameterType.UrlSegment); // Reply To if (emailMessage.ReplyToEmail.IsNotNullOrWhiteSpace()) { // Resolve any possible merge fields in the replyTo address restRequest.AddParameter("h:Reply-To", emailMessage.ReplyToEmail.ResolveMergeFields(mergeFields, emailMessage.CurrentPerson, emailMessage.EnabledLavaCommands)); } foreach (var mergeField in mergeFields) { rockMessageRecipient.MergeFields.AddOrIgnore(mergeField.Key, mergeField.Value); } // From restRequest.AddParameter("from", new MailAddress(fromAddress, fromName).ToString()); // To restRequest.AddParameter( "to", new MailAddress( rockMessageRecipient.To.ResolveMergeFields(rockMessageRecipient.MergeFields, emailMessage.CurrentPerson, emailMessage.EnabledLavaCommands), rockMessageRecipient.Name.ResolveMergeFields(rockMessageRecipient.MergeFields, emailMessage.CurrentPerson, emailMessage.EnabledLavaCommands))); // Safe Sender checks CheckSafeSender(restRequest, fromAddress, globalAttributes.GetValue("OrganizationEmail")); // CC foreach (string cc in emailMessage.CCEmails.Where(e => e != string.Empty)) { string ccRecipient = cc.ResolveMergeFields(rockMessageRecipient.MergeFields, emailMessage.CurrentPerson, emailMessage.EnabledLavaCommands); restRequest.AddParameter("cc", ccRecipient); } // BCC foreach (string bcc in emailMessage.BCCEmails.Where(e => e != string.Empty)) { string bccRecipient = bcc.ResolveMergeFields(rockMessageRecipient.MergeFields, emailMessage.CurrentPerson, emailMessage.EnabledLavaCommands); restRequest.AddParameter("bcc", bccRecipient); } // Subject string subject = ResolveText(emailMessage.Subject, emailMessage.CurrentPerson, emailMessage.EnabledLavaCommands, rockMessageRecipient.MergeFields, emailMessage.AppRoot, emailMessage.ThemeRoot).Left(998); restRequest.AddParameter("subject", subject); // Body (html) string body = ResolveText(emailMessage.Message, emailMessage.CurrentPerson, emailMessage.EnabledLavaCommands, rockMessageRecipient.MergeFields, emailMessage.AppRoot, emailMessage.ThemeRoot); body = Regex.Replace(body, @"\[\[\s*UnsubscribeOption\s*\]\]", string.Empty); restRequest.AddParameter("html", body); // Communication record for tracking opens & clicks var metaData = new Dictionary <string, string>(emailMessage.MessageMetaData); Guid?recipientGuid = null; if (emailMessage.CreateCommunicationRecord) { recipientGuid = Guid.NewGuid(); metaData.Add("communication_recipient_guid", recipientGuid.Value.ToString()); } // Additional headers AddAdditionalHeaders(restRequest, metaData); // Attachments if (emailMessage.Attachments.Any()) { using (var rockContext = new RockContext()) { var binaryFileService = new BinaryFileService(rockContext); foreach (var binaryFileId in emailMessage.Attachments.Where(a => a != null).Select(a => a.Id)) { var attachment = binaryFileService.Get(binaryFileId); if (attachment != null) { MemoryStream ms = new MemoryStream(); attachment.ContentStream.CopyTo(ms); restRequest.AddFile("attachment", ms.ToArray(), attachment.FileName); } } } } // Send it RestClient restClient = new RestClient { BaseUrl = new Uri(GetAttributeValue("BaseURL")), Authenticator = new HttpBasicAuthenticator("api", GetAttributeValue("APIKey")) }; // Call the API and get the response Response = restClient.Execute(restRequest); // Create the communication record if (emailMessage.CreateCommunicationRecord) { var transaction = new SaveCommunicationTransaction(rockMessageRecipient, emailMessage.FromName, emailMessage.FromEmail, subject, body); transaction.RecipientGuid = recipientGuid; RockQueue.TransactionQueue.Enqueue(transaction); } } catch (Exception ex) { errorMessages.Add(ex.Message); ExceptionLogService.LogException(ex); } } return(!errorMessages.Any()); }
/// <summary> /// Sends the asynchronous. /// </summary> /// <param name="rockMessage">The rock message.</param> /// <param name="mediumEntityTypeId">The medium entity type identifier.</param> /// <param name="mediumAttributes">The medium attributes.</param> /// <returns></returns> /// <exception cref="System.NotImplementedException"></exception> public async Task <SendMessageResult> SendAsync(RockMessage rockMessage, int mediumEntityTypeId, Dictionary <string, string> mediumAttributes) { var sendMessageResult = new SendMessageResult(); var smsMessage = rockMessage as RockSMSMessage; if (smsMessage != null) { // Validate From Number if (smsMessage.FromNumber == null) { sendMessageResult.Errors.Add("A From Number was not provided."); return(sendMessageResult); } string accountSid = GetAttributeValue(TwilioAttributeKey.Sid); string authToken = GetAttributeValue(TwilioAttributeKey.AuthToken); TwilioClient.Init(accountSid, authToken); // Common Merge Field var mergeFields = Lava.LavaHelper.GetCommonMergeFields(null, rockMessage.CurrentPerson); foreach (var mergeField in rockMessage.AdditionalMergeFields) { mergeFields.AddOrReplace(mergeField.Key, mergeField.Value); } int?throttlingWaitTimeMS = null; if (this.IsLongCodePhoneNumber(smsMessage.FromNumber.Value)) { throttlingWaitTimeMS = this.GetAttributeValue(TwilioAttributeKey.LongCodeThrottling).AsIntegerOrNull(); } List <Uri> attachmentMediaUrls = GetAttachmentMediaUrls(rockMessage.Attachments.AsQueryable()); if (throttlingWaitTimeMS.HasValue) { foreach (var recipient in rockMessage.GetRecipients()) { var result = await SendToRecipientAsync(recipient, mergeFields, smsMessage, attachmentMediaUrls, mediumEntityTypeId, mediumAttributes).ConfigureAwait(false); sendMessageResult.Errors.AddRange(result.Errors); sendMessageResult.Errors.AddRange(result.Warnings); sendMessageResult.MessagesSent += result.MessagesSent; await Task.Delay(throttlingWaitTimeMS.Value).ConfigureAwait(false); } } else { var sendingTask = new List <Task <SendMessageResult> >(); using (var mutex = new SemaphoreSlim(MaxParallelization)) { foreach (var recipient in rockMessage.GetRecipients()) { var startMutexWait = System.Diagnostics.Stopwatch.StartNew(); await mutex.WaitAsync().ConfigureAwait(false); sendingTask.Add(ThrottleHelper.ThrottledExecute(() => SendToRecipientAsync(recipient, mergeFields, smsMessage, attachmentMediaUrls, mediumEntityTypeId, mediumAttributes), mutex)); } /* * Now that we have fired off all of the task, we need to wait for them to complete, get their results, * and then process that result. Once all of the task have been completed we can continue. */ while (sendingTask.Count > 0) { var completedTask = await Task.WhenAny(sendingTask).ConfigureAwait(false); sendingTask.Remove(completedTask); var result = await completedTask.ConfigureAwait(false); sendMessageResult.Errors.AddRange(result.Errors); sendMessageResult.Errors.AddRange(result.Warnings); sendMessageResult.MessagesSent += result.MessagesSent; } } } } return(sendMessageResult); }
/// <summary> /// Sends the specified rock message. /// </summary> /// <param name="rockMessage">The rock message.</param> /// <param name="mediumEntityTypeId">The medium entity type identifier.</param> /// <param name="mediumAttributes">The medium attributes.</param> /// <param name="errorMessages">The error messages.</param> /// <returns></returns> public override bool Send(RockMessage rockMessage, int mediumEntityTypeId, Dictionary <string, string> mediumAttributes, out List <string> errorMessages) { errorMessages = new List <string>(); var smsMessage = rockMessage as RockSMSMessage; if (smsMessage != null) { // Validate From Number if (smsMessage.FromNumber == null) { errorMessages.Add("A From Number was not provided."); return(false); } string accountSid = GetAttributeValue("SID"); string authToken = GetAttributeValue("Token"); TwilioClient.Init(accountSid, authToken); // Common Merge Field var mergeFields = Lava.LavaHelper.GetCommonMergeFields(null, rockMessage.CurrentPerson); foreach (var mergeField in rockMessage.AdditionalMergeFields) { mergeFields.AddOrReplace(mergeField.Key, mergeField.Value); } int?throttlingWaitTimeMS = null; if (this.IsLongCodePhoneNumber(smsMessage.FromNumber.Value)) { throttlingWaitTimeMS = this.GetAttributeValue("Long-CodeThrottling").AsIntegerOrNull(); } List <Uri> attachmentMediaUrls = GetAttachmentMediaUrls(rockMessage.Attachments.AsQueryable()); foreach (var recipient in rockMessage.GetRecipients()) { try { foreach (var mergeField in mergeFields) { recipient.MergeFields.AddOrIgnore(mergeField.Key, mergeField.Value); } CommunicationRecipient communicationRecipient = null; using (var rockContext = new RockContext()) { CommunicationRecipientService communicationRecipientService = new CommunicationRecipientService(rockContext); int?recipientId = recipient.CommunicationRecipientId; if (recipientId != null) { communicationRecipient = communicationRecipientService.Get(recipientId.Value); } string message = ResolveText(smsMessage.Message, smsMessage.CurrentPerson, communicationRecipient, smsMessage.EnabledLavaCommands, recipient.MergeFields, smsMessage.AppRoot, smsMessage.ThemeRoot); Person recipientPerson = ( Person )recipient.MergeFields.GetValueOrNull("Person"); // Create the communication record and send using that if we have a person since a communication record requires a valid person. Otherwise just send without creating a communication record. if (rockMessage.CreateCommunicationRecord && recipientPerson != null) { var communicationService = new CommunicationService(rockContext); Rock.Model.Communication communication = communicationService.CreateSMSCommunication(smsMessage.CurrentPerson, recipientPerson?.PrimaryAliasId, message, smsMessage.FromNumber, string.Empty, smsMessage.communicationName); // Since we just created a new communication record, we need to move any attachments from the rockMessage // to the communication's attachments since the Send method below will be handling the delivery. if (attachmentMediaUrls.Any()) { foreach (var attachment in rockMessage.Attachments.AsQueryable()) { communication.AddAttachment(new CommunicationAttachment { BinaryFileId = attachment.Id }, CommunicationType.SMS); } } rockContext.SaveChanges(); Send(communication, mediumEntityTypeId, mediumAttributes); continue; } else { MessageResource response = SendToTwilio(smsMessage.FromNumber.Value, null, attachmentMediaUrls, message, recipient.To); if (response.ErrorMessage.IsNotNullOrWhiteSpace()) { errorMessages.Add(response.ErrorMessage); } if (communicationRecipient != null) { rockContext.SaveChanges(); } } } } catch (Exception ex) { errorMessages.Add(ex.Message); ExceptionLogService.LogException(ex); } if (throttlingWaitTimeMS.HasValue) { System.Threading.Tasks.Task.Delay(throttlingWaitTimeMS.Value).Wait(); } } } return(!errorMessages.Any()); }
/// <summary> /// Sends the specified rock message. /// </summary> /// <param name="rockMessage">The rock message.</param> /// <param name="mediumEntityTypeId">The medium entity type identifier.</param> /// <param name="mediumAttributes">The medium attributes.</param> /// <param name="errorMessages">The error messages.</param> /// <returns></returns> public override bool Send(RockMessage rockMessage, int mediumEntityTypeId, Dictionary <string, string> mediumAttributes, out List <string> errorMessages) { errorMessages = new List <string>(); var pushMessage = rockMessage as RockPushMessage; if (pushMessage != null) { // Common Merge Field var mergeFields = Lava.LavaHelper.GetCommonMergeFields(null, rockMessage.CurrentPerson); foreach (var mergeField in rockMessage.AdditionalMergeFields) { mergeFields.AddOrReplace(mergeField.Key, mergeField.Value); } var recipients = rockMessage.GetRecipients(); RockContext rockContext = new RockContext(); if (pushMessage.SendSeperatelyToEachRecipient) { foreach (var recipient in recipients) { try { foreach (var mergeField in mergeFields) { recipient.MergeFields.AddOrIgnore(mergeField.Key, mergeField.Value); } Person recipientPerson = ( Person )recipient.MergeFields.GetValueOrNull("Person"); string personAliasId = recipientPerson.Aliases.FirstOrDefault().Id.ToString(); PushMessage(new List <string> { personAliasId }, pushMessage, recipient.MergeFields); } catch (Exception ex) { errorMessages.Add(ex.Message); ExceptionLogService.LogException(ex, null); } } } else { try { foreach (var recipient in recipients) { Person recipientPerson = (Person)mergeFields.GetValueOrNull("Person"); string personAliasId = recipientPerson.Aliases.FirstOrDefault().Id.ToString(); recipient.MergeFields.Add("PersonAliasId", personAliasId); } PushMessage(recipients.Select(r => r.MergeFields.GetValueOrNull("PersonAliasId").ToString()).ToList(), pushMessage, mergeFields); } catch (Exception ex) { errorMessages.Add(ex.Message); ExceptionLogService.LogException(ex, null); } } } return(!errorMessages.Any()); }