public NotificationPayload(string notificationResource, NotificationStrings strings, NotificationCustomStrings customStrings) { SubjectResource = notificationResource + "_Subject"; BodyResource = notificationResource + "_Body"; Strings = strings; CustomStrings = customStrings; }
public static AjaxCallResult SetBitcoinPayoutAddress(string bitcoinAddress) { AuthenticationData authData = GetAuthenticationDataAndCulture(); if (authData == null) { throw new UnauthorizedAccessException(); } // Remove whitespace from submitted address (whitespace will probably be entered in some cases) bitcoinAddress = bitcoinAddress.Replace(" ", string.Empty); // Remove a possible start of "bitcoincash:" if (bitcoinAddress.StartsWith("bitcoincash:")) { bitcoinAddress = bitcoinAddress.Substring("bitcoincash:".Length); } if (string.IsNullOrEmpty(authData.CurrentUser.BitcoinPayoutAddress)) { if (!BitcoinUtility.IsValidBitcoinAddress(bitcoinAddress)) { return(new AjaxCallResult { Success = false, DisplayMessage = "Invalid address" }); } authData.CurrentUser.BitcoinPayoutAddress = bitcoinAddress; authData.CurrentUser.BitcoinPayoutAddressTimeSet = DateTime.UtcNow.ToString(CultureInfo.InvariantCulture); // TODO: Create notifications for CEO and for user NotificationCustomStrings strings = new NotificationCustomStrings(); strings["BitcoinAddress"] = bitcoinAddress; OutboundComm userNotify = OutboundComm.CreateNotification(authData.CurrentOrganization, "BitcoinPayoutAddress_Set", strings, People.FromSingle(authData.CurrentUser)); strings["ConcernedPersonName"] = authData.CurrentUser.Canonical; OutboundComm adminNotify = OutboundComm.CreateNotification(authData.CurrentOrganization, "BitcoinPayoutAddress_Set_OfficerNotify", strings); // will send to admins of org as no people specified return(new AjaxCallResult { Success = true }); } else { // If the address is already set return(new AjaxCallResult { Success = false, DisplayMessage = "Address already set" }); } }
public static void PerformAutomated(BitcoinChain chain) { // Perform all waiting hot payouts for all orgs in the installation throw new NotImplementedException("Waiting for rewrite for Bitcoin Cash"); // TODO DateTime utcNow = DateTime.UtcNow; foreach (Organization organization in Organizations.GetAll()) { // If this org doesn't do hotwallet, continue if (organization.FinancialAccounts.AssetsBitcoinHot == null) { continue; } Payouts orgPayouts = Payouts.Construct(organization); Payouts bitcoinPayouts = new Payouts(); Dictionary <string, Int64> satoshiPayoutLookup = new Dictionary <string, long>(); Dictionary <string, Int64> nativeCentsPayoutLookup = new Dictionary <string, long>(); Dictionary <int, Int64> satoshiPersonLookup = new Dictionary <int, long>(); Dictionary <int, Int64> nativeCentsPersonLookup = new Dictionary <int, long>(); Int64 satoshisTotal = 0; string currencyCode = organization.Currency.Code; // For each ready payout that can automate, add an output to a constructed transaction TransactionBuilder txBuilder = null; // TODO TODO TODO TODO new TransactionBuilder(); foreach (Payout payout in orgPayouts) { if (payout.ExpectedTransactionDate > utcNow) { continue; // payout is not due yet } if (payout.RecipientPerson != null && payout.RecipientPerson.BitcoinPayoutAddress.Length > 2 && payout.Account.Length < 4) { // If the payout address is still in quarantine, don't pay out yet string addressSetTime = payout.RecipientPerson.BitcoinPayoutAddressTimeSet; if (addressSetTime.Length > 4 && DateTime.Parse(addressSetTime, CultureInfo.InvariantCulture).AddHours(48) > utcNow) { continue; // still in quarantine } // Test the payout address - is it valid and can we handle it? if (!BitcoinUtility.IsValidBitcoinAddress(payout.RecipientPerson.BitcoinPayoutAddress)) { // Notify person that address is invalid, then clear it NotificationStrings primaryStrings = new NotificationStrings(); NotificationCustomStrings secondaryStrings = new NotificationCustomStrings(); primaryStrings[NotificationString.OrganizationName] = organization.Name; secondaryStrings["BitcoinAddress"] = payout.RecipientPerson.BitcoinPayoutAddress; OutboundComm.CreateNotification(organization, NotificationResource.BitcoinPayoutAddress_Bad, primaryStrings, secondaryStrings, People.FromSingle(payout.RecipientPerson)); payout.RecipientPerson.BitcoinPayoutAddress = string.Empty; continue; // do not add this payout } // Ok, so it seems we're making this payout at this time. bitcoinPayouts.Add(payout); int recipientPersonId = payout.RecipientPerson.Identity; if (!satoshiPersonLookup.ContainsKey(recipientPersonId)) { satoshiPersonLookup[recipientPersonId] = 0; nativeCentsPersonLookup[recipientPersonId] = 0; } nativeCentsPersonLookup[recipientPersonId] += payout.AmountCents; // Find the amount of satoshis for this payout if (organization.Currency.IsBitcoinCore) { satoshiPayoutLookup[payout.ProtoIdentity] = payout.AmountCents; nativeCentsPayoutLookup[payout.ProtoIdentity] = payout.AmountCents; satoshisTotal += payout.AmountCents; satoshiPersonLookup[recipientPersonId] += payout.AmountCents; } else { // Convert currency Money payoutAmount = new Money(payout.AmountCents, organization.Currency); Int64 satoshis = payoutAmount.ToCurrency(Currency.BitcoinCore).Cents; satoshiPayoutLookup[payout.ProtoIdentity] = satoshis; nativeCentsPayoutLookup[payout.ProtoIdentity] = payout.AmountCents; satoshisTotal += satoshis; satoshiPersonLookup[recipientPersonId] += satoshis; } } else if (payout.RecipientPerson != null && payout.RecipientPerson.BitcoinPayoutAddress.Length < 3 && payout.Account.Length < 4) { // There is a payout for this person, but they don't have a bitcoin payout address set. Send notification to this effect once a day. if (utcNow.Minute != 0) { continue; } if (utcNow.Hour != 12) { continue; } NotificationStrings primaryStrings = new NotificationStrings(); primaryStrings[NotificationString.OrganizationName] = organization.Name; OutboundComm.CreateNotification(organization, NotificationResource.BitcoinPayoutAddress_PleaseSet, primaryStrings, People.FromSingle(payout.RecipientPerson)); } else if (payout.Account.StartsWith("bitcoin:")) { } } if (bitcoinPayouts.Count == 0) { // no automated payments pending for this organization, nothing more to do continue; } // We now have our desired payments. The next step is to find enough inputs to reach the required amount (plus fees; we're working a little blind here still). BitcoinTransactionInputs inputs = null; Int64 satoshisMaximumAnticipatedFees = BitcoinUtility.GetRecommendedFeePerThousandBytesSatoshis(chain) * 20; // assume max 20k transaction size try { inputs = BitcoinUtility.GetInputsForAmount(organization, satoshisTotal + satoshisMaximumAnticipatedFees); } catch (NotEnoughFundsException) { // If we're at the whole hour, send a notification to people responsible for refilling the hotwallet if (utcNow.Minute != 0) { continue; // we're not at the whole hour, so continue with next org instead } // Send urgent notification to top up the damn wallet so we can make payments NotificationStrings primaryStrings = new NotificationStrings(); primaryStrings[NotificationString.CurrencyCode] = organization.Currency.Code; primaryStrings[NotificationString.OrganizationName] = organization.Name; NotificationCustomStrings secondaryStrings = new NotificationCustomStrings(); Int64 satoshisAvailable = HotBitcoinAddresses.ForOrganization(organization).BalanceSatoshisTotal; secondaryStrings["AmountMissingMicrocoinsFloat"] = ((satoshisTotal - satoshisAvailable + satoshisMaximumAnticipatedFees) / 100.0).ToString("N2"); if (organization.Currency.IsBitcoinCore) { secondaryStrings["AmountNeededFloat"] = ((satoshisTotal + satoshisMaximumAnticipatedFees) / 100.0).ToString("N2"); secondaryStrings["AmountWalletFloat"] = (satoshisAvailable / 100.0).ToString("N2"); } else { // convert to org native currency secondaryStrings["AmountNeededFloat"] = (new Money(satoshisTotal, Currency.BitcoinCore).ToCurrency(organization.Currency).Cents / 100.0).ToString("N2"); secondaryStrings["AmountWalletFloat"] = (new Money(satoshisAvailable, Currency.BitcoinCore).ToCurrency(organization.Currency).Cents / 100.0).ToString("N2"); } OutboundComm.CreateNotification(organization, NotificationResource.Bitcoin_Shortage_Critical, primaryStrings, secondaryStrings, People.FromSingle(Person.FromIdentity(1))); continue; // with next organization } // If we arrive at this point, the previous function didn't throw, and we have enough money. // Ensure the existence of a cost account for bitcoin miner fees. organization.EnsureMinerFeeAccountExists(); // Add the inputs to the transaction. txBuilder = txBuilder.AddCoins(inputs.Coins); txBuilder = txBuilder.AddKeys(inputs.PrivateKeys); Int64 satoshisInput = inputs.AmountSatoshisTotal; // Add outputs and prepare notifications Int64 satoshisUsed = 0; Dictionary <int, List <string> > notificationSpecLookup = new Dictionary <int, List <string> >(); Dictionary <int, List <Int64> > notificationAmountLookup = new Dictionary <int, List <Int64> >(); Payout masterPayoutPrototype = Payout.Empty; HotBitcoinAddress changeAddress = HotBitcoinAddress.OrganizationWalletZero(organization, BitcoinChain.Core); // TODO: CHAIN! foreach (Payout payout in bitcoinPayouts) { int recipientPersonId = payout.RecipientPerson.Identity; if (!notificationSpecLookup.ContainsKey(recipientPersonId)) { notificationSpecLookup[recipientPersonId] = new List <string>(); notificationAmountLookup[recipientPersonId] = new List <Int64>(); } notificationSpecLookup[recipientPersonId].Add(payout.Specification); notificationAmountLookup[recipientPersonId].Add(payout.AmountCents); if (payout.RecipientPerson.BitcoinPayoutAddress.StartsWith("1")) // regular address { txBuilder = txBuilder.Send(new BitcoinPubKeyAddress(payout.RecipientPerson.BitcoinPayoutAddress), new Satoshis(satoshiPayoutLookup[payout.ProtoIdentity])); } else if (payout.RecipientPerson.BitcoinPayoutAddress.StartsWith("3")) // multisig { txBuilder = txBuilder.Send(new BitcoinScriptAddress(payout.RecipientPerson.BitcoinPayoutAddress, Network.Main), new Satoshis(satoshiPayoutLookup[payout.ProtoIdentity])); } else { throw new InvalidOperationException("Unhandled bitcoin address type in Payouts.PerformAutomated(): " + payout.RecipientPerson.BitcoinPayoutAddress); } satoshisUsed += satoshiPayoutLookup[payout.ProtoIdentity]; payout.MigrateDependenciesTo(masterPayoutPrototype); } // Set change address to wallet slush txBuilder.SetChange(new BitcoinPubKeyAddress(changeAddress.Address)); // Add fee int transactionSizeBytes = txBuilder.EstimateSize(txBuilder.BuildTransaction(false)) + inputs.Count; // +inputs.Count for size variability Int64 feeSatoshis = (transactionSizeBytes / 1000 + 1) * BitcoinUtility.GetRecommendedFeePerThousandBytesSatoshis(chain); txBuilder = txBuilder.SendFees(new Satoshis(feeSatoshis)); satoshisUsed += feeSatoshis; // Sign transaction - ready to execute Transaction txReady = txBuilder.BuildTransaction(true); // Verify that transaction is ready if (!txBuilder.Verify(txReady)) { // Transaction was not signed with the correct keys. This is a serious condition. NotificationStrings primaryStrings = new NotificationStrings(); primaryStrings[NotificationString.OrganizationName] = organization.Name; OutboundComm.CreateNotification(organization, NotificationResource.Bitcoin_PrivateKeyError, primaryStrings); throw new InvalidOperationException("Transaction is not signed enough"); } // Broadcast transaction BitcoinUtility.BroadcastTransaction(txReady, BitcoinChain.Cash); // Note the transaction hash string transactionHash = txReady.GetHash().ToString(); // Delete all old inputs, adjust balance for addresses (re-register unused inputs) inputs.AsUnspents.DeleteAll(); // Log the new unspent created by change (if there is any) if (satoshisInput - satoshisUsed > 0) { SwarmDb.GetDatabaseForWriting() .CreateHotBitcoinAddressUnspentConditional(changeAddress.Identity, transactionHash, +/* the change address seems to always get index 0? is this a safe assumption? */ 0, satoshisInput - satoshisUsed, /* confirmation count*/ 0); } // Register new balance of change address, should have increased by (satoshisInput-satoshisUsed) // TODO // Send notifications foreach (int personId in notificationSpecLookup.Keys) { Person person = Person.FromIdentity(personId); string spec = string.Empty; for (int index = 0; index < notificationSpecLookup[personId].Count; index++) { spec += String.Format(" * {0,-40} {1,14:N2} {2,-4}\r\n", notificationSpecLookup[personId][index], notificationAmountLookup[personId][index] / 100.0, currencyCode); } spec = spec.TrimEnd(); NotificationStrings primaryStrings = new NotificationStrings(); NotificationCustomStrings secondaryStrings = new NotificationCustomStrings(); primaryStrings[NotificationString.OrganizationName] = organization.Name; primaryStrings[NotificationString.CurrencyCode] = organization.Currency.DisplayCode; primaryStrings[NotificationString.EmbeddedPreformattedText] = spec; secondaryStrings["AmountFloat"] = (nativeCentsPersonLookup[personId] / 100.0).ToString("N2"); secondaryStrings["BitcoinAmountFloat"] = (satoshiPersonLookup[personId] / 100.0).ToString("N2"); secondaryStrings["BitcoinAddress"] = person.BitcoinPayoutAddress; // warn: potential rare race condition here OutboundComm.CreateNotification(organization, NotificationResource.Bitcoin_PaidOut, primaryStrings, secondaryStrings, People.FromSingle(person)); } // Create the master payout from its prototype Payout masterPayout = Payout.CreateBitcoinPayoutFromPrototype(organization, masterPayoutPrototype, txReady.GetHash().ToString()); // Finally, create ledger entries and notify NotificationStrings masterPrimaryStrings = new NotificationStrings(); NotificationCustomStrings masterSecondaryStrings = new NotificationCustomStrings(); masterPrimaryStrings[NotificationString.OrganizationName] = organization.Name; masterPrimaryStrings[NotificationString.CurrencyCode] = organization.Currency.DisplayCode; masterSecondaryStrings["AmountFloat"] = (new Swarmops.Logic.Financial.Money(satoshisUsed, Currency.BitcoinCore).ToCurrency( organization.Currency).Cents / 100.0).ToString("N2", CultureInfo.InvariantCulture); masterSecondaryStrings["BitcoinAmountFloat"] = (satoshisUsed / 100.0).ToString("N2", CultureInfo.InvariantCulture); masterSecondaryStrings["PaymentCount"] = bitcoinPayouts.Count.ToString("N0", CultureInfo.InvariantCulture); OutboundComm.CreateNotification(organization, NotificationResource.Bitcoin_Hotwallet_Outflow, masterPrimaryStrings, masterSecondaryStrings); // TODO: special case for native-bitcoin organizations vs. fiat-currency organizations FinancialTransaction ledgerTransaction = FinancialTransaction.Create(organization, utcNow, "Bitcoin automated payout"); if (organization.Currency.IsBitcoinCore) { ledgerTransaction.AddRow(organization.FinancialAccounts.AssetsBitcoinHot, -(masterPayoutPrototype.AmountCents + feeSatoshis), null); ledgerTransaction.AddRow(organization.FinancialAccounts.CostsBitcoinFees, feeSatoshis, null); } else { // If the ledger isn't using bitcoin natively, we need to translate the miner fee paid to ledger cents before entering it into ledger Int64 feeCentsLedger = new Money(feeSatoshis, Currency.BitcoinCore).ToCurrency(organization.Currency).Cents; ledgerTransaction.AddRow(organization.FinancialAccounts.AssetsBitcoinHot, -(masterPayoutPrototype.AmountCents + feeCentsLedger), null).AmountForeignCents = new Money(satoshisUsed, Currency.BitcoinCore); ledgerTransaction.AddRow(organization.FinancialAccounts.CostsBitcoinFees, feeCentsLedger, null); } ledgerTransaction.BlockchainHash = transactionHash; masterPayout.BindToTransactionAndClose(ledgerTransaction, null); } }
public NotificationPayload(string notificationResource, NotificationCustomStrings customStrings) : this(notificationResource, new NotificationStrings(), customStrings) { // also redirect to main ctor }
internal static void Run() { OutboundComms comms = OutboundComms.GetOpen(); foreach (OutboundComm comm in comms) { BotLog.Write(1, "CommsTx", "OutboundComm #" + comm.Identity.ToString("N0")); if (!comm.Resolved) { BotLog.Write(2, "CommsTx", "--resolving"); ICommsResolver resolver = FindResolver(comm); resolver.Resolve(comm); comm.Resolved = true; int recipientCount = comm.Recipients.Count; BotLog.Write(2, "CommsTx", "--resolved to " + recipientCount.ToString("N0") + " recipients"); if (recipientCount > 1 && comm.SenderPersonId != 0) { // "Your message has been queued for delivery and the recipients have been resolved. // Your mail will be sent to, or be attempted to sent to, [RecipientCount] people in [Geography] in [OrganizationName]." NotificationStrings notifyStrings = new NotificationStrings(); NotificationCustomStrings customStrings = new NotificationCustomStrings(); notifyStrings[NotificationString.OrganizationName] = Organization.FromIdentity(comm.OrganizationId).Name; customStrings["RecipientCount"] = comm.Recipients.Count.ToString("N0"); if (resolver is IHasGeography) { customStrings["GeographyName"] = ((IHasGeography)resolver).Geography.Localized; } OutboundComm.CreateNotification(Organization.FromIdentity(comm.OrganizationId), NotificationResource.OutboundComm_Resolved, notifyStrings, customStrings, People.FromSingle(Person.FromIdentity(comm.SenderPersonId))); } comm.StartTransmission(); continue; // continue is not strictly necessary; could continue processing the same OutboundComm after resolution } if (comm.TransmitterClass != "Swarmops.Utility.Communications.CommsTransmitterMail") { throw new NotImplementedException(); } ICommsTransmitter transmitter = new CommsTransmitterMail(); const int batchSize = 1000; OutboundCommRecipients recipients = comm.GetRecipientBatch(batchSize); PayloadEnvelope envelope = PayloadEnvelope.FromXml(comm.PayloadXml); BotLog.Write(2, "CommsTx", "--transmitting to " + recipients.Count.ToString("N0") + " recipients"); foreach (OutboundCommRecipient recipient in recipients) { try { transmitter.Transmit(envelope, recipient.Person); recipient.CloseSuccess(); } catch (OutboundCommTransmitException e) { recipient.CloseFailed(e.Description); } } if (recipients.Count < batchSize) // Was this the last batch? { comm.Open = false; BotLog.Write(2, "CommsTx", "--closing"); OutboundComm reloadedComm = OutboundComm.FromIdentity(comm.Identity); // active object doesn't update as we get results, so need to load // from database to get final counts of successes and fails if (comm.RecipientCount > 1 && comm.SenderPersonId != 0) { BotLog.Write(2, "CommsTx", "--notifying"); ICommsResolver resolver = FindResolver(comm); // "Your message to [GeographyName] has been sent to all scheduled recipients. Of the [RecipientCount] planned recipients, // [RecipientsSuccess] succeeded from Swarmops' horizon. (These can fail later for a number of reasons, from broken // computers to hospitalized recipients.) Time spent transmitting: [TransmissionTime]." NotificationStrings notifyStrings = new NotificationStrings(); NotificationCustomStrings customStrings = new NotificationCustomStrings(); notifyStrings[NotificationString.OrganizationName] = Organization.FromIdentity(comm.OrganizationId).Name; customStrings["RecipientCount"] = reloadedComm.RecipientCount.ToString("N0"); customStrings["RecipientsSuccess"] = reloadedComm.RecipientsSuccess.ToString("N0"); TimeSpan resolveTime = comm.StartTransmitDateTime - comm.CreatedDateTime; TimeSpan transmitTime = comm.ClosedDateTime - comm.StartTransmitDateTime; TimeSpan totalTime = resolveTime + transmitTime; customStrings["TransmissionTime"] = FormatTimespan(transmitTime); customStrings["ResolvingTime"] = FormatTimespan(resolveTime); customStrings["TotalTime"] = FormatTimespan(totalTime); if (resolver is IHasGeography) { customStrings["GeographyName"] = ((IHasGeography)resolver).Geography.Localized; } OutboundComm.CreateNotification(Organization.FromIdentity(comm.OrganizationId), NotificationResource.OutboundComm_Sent, notifyStrings, customStrings, People.FromSingle(Person.FromIdentity(comm.SenderPersonId))); } } } }
public static void CreateAnnualStatements(int year) { // This function is supposed to be running once a year, and summarize the past year of salaries. // Get all organizations. Organizations organizations = Organizations.GetAll(); foreach (Organization organization in organizations) { // Get all salaries for this organization (filter year in logic - this function is allowed to be expensive, rather than // adding a custom database query for this particular operation) Salaries allSalaries = ForOrganization(organization, true); Salaries yearSalaries = new Salaries(); yearSalaries.AddRange(allSalaries.Where(salary => salary.PayoutDate.Year == year)); Dictionary <int, Dictionary <int, Salary> > personSalaryLookup = new Dictionary <int, Dictionary <int, Salary> >(); // Go through the salaries and store them in the dictionary, so we can process the statement by person later foreach (Salary salary in yearSalaries) { int personId = salary.PayrollItem.PersonId; if (!personSalaryLookup.ContainsKey(personId)) { personSalaryLookup[personId] = new Dictionary <int, Salary>(); } personSalaryLookup[personId][salary.PayoutDate.Month] = salary; } // Once here, salaries are arranged by person and month. Iterate over people, create statements. foreach (int personId in personSalaryLookup.Keys) { Person person = Person.FromIdentity(personId); Int64 grossTotal = 0; Int64 subTaxTotal = 0; Int64 addTaxTotal = 0; Int64 netTotal = 0; string preFormattedStatement = string.Empty; for (int monthNumber = 1; monthNumber <= 12; monthNumber++) { string monthName = new DateTime(year, monthNumber, 1).ToString("MMM", new CultureInfo(person.PreferredCulture)); monthName = monthName.Substring(0, 3); // believe it or not some .Net localizations don't respect the three letter limit string lineItem = " " + monthName; if (personSalaryLookup[personId].ContainsKey(monthNumber) && personSalaryLookup[personId][monthNumber].GrossSalaryCents > 0) { Salary monthSalary = personSalaryLookup[personId][monthNumber]; // TODO: replace "bitcoin" with actual payout method - this is just for show at the moment lineItem += String.Format(" {0,10:N2} {1,11:N2} {2,10:N2} bitcoin", monthSalary.GrossSalaryCents / 100.0, -monthSalary.SubtractiveTaxCents / 100.0, monthSalary.NetSalaryCents / 100.0); grossTotal += monthSalary.GrossSalaryCents; addTaxTotal += monthSalary.AdditiveTaxCents; subTaxTotal += monthSalary.SubtractiveTaxCents; netTotal += monthSalary.NetSalaryCents; } preFormattedStatement += lineItem + "\r\n"; } NotificationStrings notificationStrings = new NotificationStrings(); NotificationCustomStrings customStrings = new NotificationCustomStrings(); notificationStrings[NotificationString.CurrencyCode] = organization.Currency.DisplayCode; notificationStrings[NotificationString.EmbeddedPreformattedText] = preFormattedStatement; customStrings["LastYear"] = year.ToString(CultureInfo.InvariantCulture); customStrings["GrossSalaryTotal"] = String.Format("{0,10:N2}", grossTotal / 100.0); customStrings["TaxDeductedTotal"] = String.Format("{0,11:N2}", -subTaxTotal / 100.0); customStrings["NetSalaryTotal"] = String.Format("{0,10:N2}", netTotal / 100.0); customStrings["TaxAdditiveTotalUnpadded"] = String.Format("{0:N2}", addTaxTotal / 100.0); // Send notification if gross is nonzero if (grossTotal > 0) { OutboundComm.CreateNotification(organization, NotificationResource.Salary_LastYearSummary, notificationStrings, customStrings, People.FromSingle(person)); } } } }
public void Transmit(PayloadEnvelope envelope, Person person) { // Create the renderer via reflection of the static FromXml method Assembly assembly = typeof(PayloadEnvelope).Assembly; Type payloadType = assembly.GetType(envelope.PayloadClass); if (payloadType == null) { NotificationCustomStrings customStrings = new NotificationCustomStrings(); customStrings["UnrecognizedPayloadType"] = envelope.PayloadClass; OutboundComm.CreateNotification(null, NotificationResource.System_UnrecognizedPayload, customStrings); throw new OutboundCommTransmitException("Unrecognized or uninstantiable payload type: " + envelope.PayloadClass); } var methodInfo = payloadType.GetMethod("FromXml", BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy); ICommsRenderer renderer = (ICommsRenderer)(methodInfo.Invoke(null, new object[] { envelope.PayloadXml })); RenderedComm comm = renderer.RenderComm(person); MailMessage mail = new MailMessage(); // This is a rather simple mail (no images or stuff like that) mail.Subject = (string)comm[CommRenderPart.Subject]; mail.SubjectEncoding = Encoding.UTF8; try { mail.From = new MailAddress((string)comm[CommRenderPart.SenderMail], (string)comm[CommRenderPart.SenderName], Encoding.UTF8); // SPECIAL CASE for sandbox mails -- ugly code but wtf if (person.Identity == 1 && PilotInstallationIds.DevelopmentSandbox == SystemSettings.InstallationId && mail.Subject.Contains("|")) { string[] separated = mail.Subject.Split('|'); mail.Subject = separated[0]; mail.To.Add(new MailAddress(separated[1], "Swarmops Sandbox Administrator")); } else // regular case to be used... like everywhere else except for the sandbox test { if (string.IsNullOrWhiteSpace(person.Mail)) { throw new OutboundCommTransmitException("No valid mail address for " + person.Canonical); } mail.To.Add(new MailAddress(person.Mail, person.Name)); } } catch (ArgumentException e) { // Address failure -- either sender or recipient _cacheReloadTime = Constants.DateTimeLow; throw new OutboundCommTransmitException("Cannot send mail to " + person.Mail, e); } string mailBodyText = (string)comm[CommRenderPart.BodyText]; mailBodyText = mailBodyText.Replace("[Addressee]", person.Canonical); string mailBodyHtml = comm.ContainsKey(CommRenderPart.BodyHtml)? (string)comm[CommRenderPart.BodyHtml]: string.Empty; mailBodyHtml = mailBodyHtml.Replace("[Addressee]", person.Canonical); mail.Body = mailBodyText; mail.BodyEncoding = Encoding.UTF8; string smtpServer = _smtpServerCache; int smtpPort = _smtpPortCache; string smtpUser = _smtpUserCache; string smtpPassword = _smtpPasswordCache; DateTime now = DateTime.Now; if (now > _cacheReloadTime) { smtpServer = _smtpServerCache = SystemSettings.SmtpHost; smtpPort = _smtpPortCache = SystemSettings.SmtpPort; smtpUser = _smtpUserCache = SystemSettings.SmtpUser; smtpPassword = _smtpPasswordCache = SystemSettings.SmtpPassword; _cacheReloadTime = now.AddMinutes(5); } if (string.IsNullOrEmpty(smtpServer)) { smtpServer = "localhost"; smtpPort = 25; // For development use only - invalidate cache instead of this, forcing re-reload _cacheReloadTime = Constants.DateTimeLow; } SmtpClient mailClient = new SmtpClient(smtpServer, smtpPort); if (!string.IsNullOrEmpty(smtpUser)) { mailClient.Credentials = new System.Net.NetworkCredential(smtpUser, smtpPassword); } try { mailClient.Send(mail); } catch (Exception e) { _cacheReloadTime = Constants.DateTimeLow; throw new OutboundCommTransmitException("Cannot send mail to " + person.Mail, e); } }
public static OutboundComm CreateNotification(Organization organization, string notificationResourceString, NotificationStrings strings, NotificationCustomStrings customStrings, People recipients = null) { if (recipients == null) { recipients = People.FromSingle(Person.FromIdentity(1)); // Initial Admin recipient } if (organization != null) { strings[NotificationString.OrganizationName] = organization.Name; //strings[NotificationString.OrganizationMailPrefix] = organization.MailPrefix; // TODO // TODO: Change to org admins } OutboundComm comm = OutboundComm.Create(null, null, organization, null, null, CommTransmitterClass.CommsTransmitterMail, new PayloadEnvelope(new NotificationPayload(notificationResourceString, strings, customStrings)).ToXml(), OutboundCommPriority.Low); foreach (Person person in recipients) { comm.AddRecipient(person); } comm.Resolved = true; return(comm); }
public static OutboundComm CreateNotification(Organization organization, string notificationResourceString, NotificationCustomStrings customStrings, People recipients = null) { return(CreateNotification(organization, notificationResourceString, new NotificationStrings(), customStrings, recipients)); }