How to send a iCal invite with Mailgun Rest API (C#) - c#

I am attempting to add a calendar invite in iCal format to an email sent via the MailGun API. This is what i have so far:
var request = new RestRequest();
request.AddParameter("domain", this.domain, ParameterType.UrlSegment);
request.Resource = "{domain}/messages";
request.AddParameter("from", contactDetails.SenderAddress);
request.AddParameter("to", contactDetails.RecipientAddress);
request.AddParameter("subject", message.Subject);
request.AddParameter("text", message.TextBody);
request.AddParameter("html", message.HtmlBody);
if (!string.IsNullOrWhiteSpace(message.IcalAttachment))
{
request.AddFileBytes("attachment",
Encoding.UTF8.GetBytes(message.IcalAttachment),
"invite.ics",
"text/calendar");
}
request.Method = Method.POST;
return request;
This results in the calendar being included in the email as an attachment, not an alternative view of the email. The attachment works fine in gmail however in Outlook it appears as an attachment file that you must first click on, then agree to adding the calendar to the Outlook calendar. Is there another way to use the REST api so that the calendar invites are sent correctly, as alternative email views?
To be clear, this is how I would send a calendar invite using .Net SmtpClient:
var contentType = new ContentType("text/calendar");
if (contentType.Parameters != null)
{
contentType.Parameters.Add("method", "REQUEST");
contentType.CharSet = "UTF-8";
}
// this is the same way you add a html view to the message
request.AlternateViews.Add(
AlternateView.CreateAlternateViewFromString(
message.IcalAttachment,
contentType));

Special thanks to Mailgun support for pointing me in the right direction. The relevant part or their response was:
You can use the /message.mime endpoint to construct the MIME for the calendar invite:
https://documentation.mailgun.com/api-sending.html#sending
Creating a mime message isnt as easy as simply using their /message endpoint but there are several .net libraries available to do this. I used MimeKit in this example.
var request = new RestRequest();
request.AddParameter("domain", this.domain, ParameterType.UrlSegment);
request.Resource = "{domain}/messages.mime";
request.AddParameter("to", contactDetails.RecipientAddress);
request.AddFile(
"message",
Encoding.UTF8.GetBytes(BuildMimeContent(message)),
"message.mime");
request.Method = Method.POST;
return request;
The mime content that I want to create will contain a multipart/mixed body, which will in turn contain a multipart/alternative as well as every attachment. The calendar invite will actually be attached twice, as a alternative view and as an attachment. This is to aid in compatibilitiy across different email clients.
The implementation of BuildMimeContent(message) looks like the following:
// create the alternative views
var textBody = new TextPart("plain") { Text = message.TextBody };
var htmlBody = new TextPart("html") { Text = message.HtmlBody };
// add views to the multipart/alternative
var alternative = new Multipart("alternative");
alternative.Add(textBody);
alternative.Add(htmlBody);
if (!string.IsNullOrWhiteSpace(message.CalendarInvite))
{
// also add the calendar as an alternative view
// encoded as base64, but 7bit will also work
var calendarBody = new TextPart("calendar")
{
Text = message.CalendarInvite,
ContentTransferEncoding = ContentEncoding.Base64
};
// most clients wont recognise the alternative view without the
// method=REQUEST header
calendarBody.ContentType.Parameters.Add("method", "REQUEST");
alternative.Add(calendarBody);
}
// create the multipart/mixed that will contain the multipart/alternative
// and all attachments
var multiPart = new Multipart("mixed") { alternative };
if (!string.IsNullOrWhiteSpace(message.CalendarInvite))
{
// add the calendar as an attachment
var calAttachment = new MimePart("application", "ics")
{
ContentDisposition = new ContentDisposition(ContentDisposition.Attachment),
ContentTransferEncoding = ContentEncoding.Base64,
FileName = "invite.ics",
ContentObject = new ContentObject(GenerateStreamFromString(message.CalendarInvite))
};
multiPart.Add(calAttachment);
}
// TODO: Add any other attachements to 'multipart' here.
// build final mime message
var mimeMessage = new MimeMessage();
mimeMessage.From.Add(GetMimeAddress(message.MessageInfo.SenderName, message.MessageInfo.SenderAddress));
mimeMessage.To.Add(GetMimeAddress(message.MessageInfo.RecipientName, message.MessageInfo.RecipientAddress));
mimeMessage.Subject = message.Subject;
mimeMessage.Body = multiPart;
// parse and return mime message
return mimeMessage.ToString();
Warning for people testing with Office 365
Office365 is extremely picky when it comes to validating calendar invites. In order to not get a message like the one below, you will need to ensure that the vCal's organizer email address matches the email's from address. This is not possible if you are using mailgun's sandbox test environment.

Related

Create Master Category Via Microsoft Graph API

Im Trying to create a new category via an event for outlook. Below is what I have so far.
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", AdviserBearerToken);
client.DefaultRequestHeaders.Accep.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var categoryName = new List<string>();
categoryName.Add("New Event");
var startTime = new Time();
var endTime = new Time();
startTime.DateTime = "2016-07-15T15:00:00.0000000";
startTime.TimeZone = "UTC";
endTime.DateTime = "2016-07-15T15:30:00.0000000";
endTime.TimeZone = "UTC";
var eventModel = new EventModelForGraph
{
categories = categoryName,
subject = "This is an event",
Start = startTime,
End = endTime
};
var serializedObject = JsonConvert.SerializeObject(eventModel);
var createBody = new StringContent(serializedObject, System.Text.Encoding.UTF8, "application/json");
var response = await client.PostAsync("https://graph.microsoft.com/v1.0/me/calendar/events", createBody);
var responseString = await response.Content.ReadAsStringAsync();
}
The event shows up in the calendar and the category as the header but it is not listed under the categorize tab which leads me to my question.
Is it possible to create such a category using the API?
I know this is an older question, but I was looking into the same thing and figured I'd post an update. This is now possible with the current version of the Graph API. You can see the documentation here from MSDN. You can create categories by sending a POST API request:
POST https://graph.microsoft.com/beta/me/outlook/masterCategories
Content-type: application/json
Content-Length: 70
{
"displayName":"Project expenses",
"color":"preset9"
}
After the category is created, you can assign it when you create your event by adding the category's displayName property to the categories collection of the item.
You can find more details about when these API endpoints were added here and more details about categories here.
No, you cannot add categories to the master category list via the REST API. You cannot add them directly via any API.
However, you CAN modify the list if you're willing to manipulate the XML directly. The gory details are documented in MS-OXOCFG. You can use EWS for example to access the config item.
This would be a great feature to add to the REST API. You should suggest it on UserVoice.

Attaching to sendgrid api not working when using sendgrid mail helper

I have looked through the questions i can find on here about attaching a file to a sendgrid email but none seem to have the issue I am.
My question is is this. How do you send an email with an attachment in sendgrid using the api?
dynamic sg = new SendGridAPIClient(apiKey);
var from = new SendGrid.Helpers.Mail.Email("jkennedy#domain.com");
var subject = "Hello World from the SendGrid C# Library!";
var to = new SendGrid.Helpers.Mail.Email(toAddress);
var content = new Content("multipart/form-data", "Textual content");
var attachment = new Attachment {Filename = attachmentPath };
var mail = new Mail(from, subject, to, content);
var ret = mail.Get();
mail.AddAttachment(attachment);
dynamic response = await sg.client.mail.send.post(requestBody: ret);
If i put the mail.attachment after the get the mail sends but there is no attachment. If i put the addattachment line before the get i get a "bad request" message.
I have yet to find an example of exactly how to do this.
Also, the path to the file is c:\tblaccudatacounts.csv
After struggling with this for a couple hours, I found an answer using sendgrid's V3 API. Here's what I learned.
In your example, you call var ret = mail.Get(); before adding the attachment. Since mail.Get() is essentially serializing the mail object into the Json format SendGrid is expecting, adding the attachment after the mail.Get() call will not actually add it to the mail object.
The other thing you should know is that the API doesn't have a way of simply taking the file path as an input (At least that I can find, I hope someone can correct me). You need to manually set at least the content (as a base 64 string) and filename. You can find more information here.
Here is my working solution:
string apiKey = "your API Key";
dynamic sg = new SendGridAPIClient(apiKey);
Email from = new Email("your#domain.com");
string subject = "Hello World from the SendGrid CSharp Library!";
Email to = new Email("destination#there.com");
Content body = new Content("text/plain", "Hello, Email!");
Mail mail = new Mail(from, subject, to, body);
byte[] bytes = File.ReadAllBytes("C:/dev/datafiles/testData.txt");
string fileContentsAsBase64 = Convert.ToBase64String(bytes);
var attachment = new Attachment
{
Filename = "YourFile.txt",
Type = "txt/plain",
Content = fileContentsAsBase64
};
mail.AddAttachment(attachment);
dynamic response = await sg.client.mail.send.post(requestBody: mail.Get());
I figure it out. I was using a helper written by a third party. I went with what with SendGrid actually suggested. See code below that is now working.
var myMessage = new SendGridMessage {From = new MailAddress("info#email.com")};
myMessage.AddTo("Jeff Kennedy <info#info.com>");
myMessage.Subject = "test email";
myMessage.Html = "<p>See Attachedment for Lead</p>";
myMessage.Text = "Hello World plain text!";
myMessage.AddAttachment("C:\\tblaccudatacounts.csv");
var apiKey = "apikey given by sendgrid";
var transportWeb = new Web(apiKey);
await transportWeb.DeliverAsync(myMessage);

Attachment without filename

When using the System.Net.Mail namespace to send a e-mail with attachment to any Yahoo account the attachment is downloaded with 'untitled' name instead the file name.
In the Yahoo Mail interface the attachment looks with the correct name but when you download it the download name goes to 'untitled' for all attachments. The same e-mail message works fine with Gmail, Outlook.com, Windows Live Mail and other clients.
Looking the raw message it constains a content-type with name but without filename attribute. The Yahoo works fine if the filename attribute is set but C# library don't use this.
That's the header generated by C# for attachments:
Content-Type: application/octet-stream; name=test.pdf
That's the header that works with Yahoo:
Content-Type: application/octet-stream; name=file2; filename=test.pdf
Anyone get this problem so far? Is there a work arround for C# default mail sending?
using (var message = new MailMessage("from#domain", "to#yahoo.com.br", "Test with attachment", "Test with attachment"))
{
var attachment = new Attachment(#"c:\temp\test.pdf"); // Same result using stream instead path to file.
attachment.Name = "test.pdf"; // Same result without this line.
message.Attachments.Add(attachment);
using (var smtp = new SmtpClient("smtp.domain", 587))
{
smtp.Credentials = new NetworkCredential("from#domain", "password");
smtp.Send(message);
}
}
I found a solution:
attachment.ContentDisposition.FileName = "test.pdf";
This add the missing filename attribute in the raw e-mail message and solve the Yahoo limitation.
Have you tried explicitly specifying the content type?
var attachment = new Attachment(... , MediaTypeNames.Application.Octet);

Mailgun sending attachment with RestSharp

I'm using RestSharp to try and send an attachment with the Mailgun API. I have tried attaching from both a file in the system using a hardcoded path and also from a binary file stored in the database using ToArray() method on the varbinary(MAX) (SQL Server) property both with no success.
The attachment technically sends, but when the email arrives in my inbox the file size is always roughly 302bytes big and is always corrupt. I have tried 3 different files and get the same problem each time.
The rest of the email sends, delivers and displays fine. It's just the attachments that are broken.
Breakdown of code:
// Doesnt work(Data property is varbinary(MAX)
request.AddFileBytes("attachment",databaseModel.Data.ToArray(),databaseModel.Filename, "multipart/form-data");
// Also doesnt work(Data property is varbinary(MAX)
request.AddFile("attachment",databaseModel.Data.ToArray(),databaseModel.Filename, "multipart/form-data");
// Also doesnt work
var path = #"D:\Template.pdf";
request.AddFile("attachment",path,"multipart/form-data");
This code works:
public static void Main(string[] args)
{
Console.WriteLine(SendSimpleMessage().Content.ToString());
Console.ReadLine();
}
public static IRestResponse SendSimpleMessage()
{
var path1 = #"C:\Users\User\Pictures\website preview";
var fileName = "Learn.png";
RestClient client = new RestClient();
client.BaseUrl = new Uri("https://api.mailgun.net/v3");
client.Authenticator =
new HttpBasicAuthenticator("api",
"key-934345306fead7de0296ec2fb96a143");
RestRequest request = new RestRequest();
request.AddParameter("domain", "mydomain.info", ParameterType.UrlSegment);
request.Resource = "{domain}/messages";
request.AddParameter("from", "Excited User <example#mydomain.info>");
request.AddParameter("to", "peter.cech#gmail.com");
request.AddParameter("subject", "Hello");
request.AddParameter("text", "Testing some Mailgun awesomness! This is all about the text only. Just testing the text of this email.";
request.AddFile("attachment", Path.Combine(path1,fileName));
request.Method = Method.POST;
return client.Execute(request);
}
I figured it out..
Not supposed to add "multipart/form-data" on the request.AddFile();
Removing this fixes the problem.

Attaching PDF file in email

I need to attach an receipt in the email which has been generated dynamically. I'm unable to attach the pdf file, it denotes me that path is not valid.
Here is my code:
public static IRestResponse SendConfirmationEmail(string emailaddress,string subject,string body)
{
RestClient client = new RestClient();
client.BaseUrl = "https://123456";
client.Authenticator = new HttpBasicAuthenticator("api", "key-abcdef12345huj");
RestRequest request = new RestRequest();
request.AddParameter("domain", "abc.com", ParameterType.UrlSegment);
request.Resource = "{domain}/messages";
request.AddParameter("from", "abc <abc#xyz.com>");
System.Net.Mail.Attachment attachment;
attachment = new System.Net.Mail.Attachment("~/users/Receipts/abc-414.pdf");
request.AddParameter("attachment",attachment);
request.AddParameter("c", emailaddress);
request.AddParameter("to", emailaddress);
request.AddParameter("subject", subject);
request.AddParameter("html", body);
request.Method = Method.POST;
return client.Execute(request);
}
Can any one help me out on this issue?
Thank you
try in this way
attachment = new System.Net.Mail.Attachment(HttpContext.Current.Server.MapPath("~/users/Receipts/abc-414.pdf"));
use Server.MapPath method that returns the physical file path that corresponds to the specified virtual path on the Web server.
I see that you are sending these parameters in a POST request. Did you check the process of serializing-deserializing? maybe there is something wrong in this step and that parameter is ignored

Categories